Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new settings to set the contact email address #2279

Merged
merged 5 commits into from
Aug 16, 2017

Conversation

Pierre-Assemat
Copy link

Fixes #2266

Copy link
Member

@danlamanna danlamanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this is WIP but I decided to leave a couple of comments.

@setting_utilities.validator(SettingKey.CONTACT_EMAIL_ADDRESS)
def validateCoreContactEmailAddress(doc):
if not doc['value']:
raise ValidationException('Email from address must not be blank.', 'value')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a configuration option for specifying a regex email addresses must conform to, that can probably be used here as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the discussion here we probably want to support more than a simple email address regex (especially since the default EMAIL_FROM_ADDRESS default already uses a proper name component).

The space and URL encoding described by the RFC also should be done, but probably not in the validator (as that will make the value hard to edit). The best place is probably in the Pug template.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I skimmed through RFC6068, and it appears that some degree of escaping is definitely necessary. I'm not yet clear on whether the syntax with a proper name is legal, though I expect most clients would tolerate it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to address this.

events.bind('model.setting.save.after', CoreEventHandler.WEBROOT_SETTING_CHANGE,
self._onSettingSave)

def _onSettingSave(self, event):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sort of thing (which exists in other places in Girder thanks to yours truly) will not work in a distributed installation.

For the purposes of this PR this might be fine, but I think that #2274 might try to remove these patterns. For more detail about it there's a thread on girder-developers with the subject "Signalling between multiple Girder servers".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, as part of fully implementing #2274, the WebrootBase.indexHtml should be switched to use a proper cache. I see nothing more that can be done on this PR.

@@ -1,6 +1,6 @@
.g-footer-links
a#g-about-link(href="http://girder.readthedocs.org/en/latest/user-guide.html") About
a#g-contact-link(href="mailto:kitware@kitware.com") Contact
a#g-contact-link(href=href) Contact
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be something like contactHref?

@@ -40,6 +40,7 @@ var App = View.extend({
this._started = false;
settings = settings || {};
if (settings.start === undefined || settings.start) {
this.contactEmail = settings.contactEmail;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should always store this setting in an instance variable, even if we're not starting the app.

render: function () {
this.$el.html(LayoutFooterTemplate({
apiRoot: getApiRoot()
apiRoot: getApiRoot(),
href: this.emailHref
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be something like contactHref?

@@ -9,9 +9,16 @@ import 'girder/stylesheets/layout/footer.styl';
* This view shows the footer in the layout.
*/
var LayoutFooterView = View.extend({

initialize: function (settings) {
var mail = settings.contactEmail; // Has to change
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a fallback value, in case settings.contactEmail is not provided.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, use let or const.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fallback value can be the default email ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but maybe not. If a default is needed for this widget, it means that it's being used in another application that didn't pass enough parameters at all.

Maybe if no user is passed at all, then don't use an href? What do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a blank ('') instead, but do you mean test if settings.contactEmail is provided and if not don't use href and hide the contact link ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a good idea.

This case will only apply when a some external code imports and reuses this view, but it should be handled gracefully.

Pierre Assemat added 2 commits August 11, 2017 12:44
This commit take both the client and the server side in adding new settings
This test verify the emailAddress setting in the webroot.mako
@Pierre-Assemat Pierre-Assemat force-pushed the set-email-contact-link branch 2 times, most recently from e48b126 to a0b2c2c Compare August 14, 2017 16:07
@Pierre-Assemat Pierre-Assemat changed the title WIP : Add new settings to set the contact email address Add new settings to set the contact email address Aug 14, 2017
@@ -9,9 +9,18 @@ import 'girder/stylesheets/layout/footer.styl';
* This view shows the footer in the layout.
*/
var LayoutFooterView = View.extend({

initialize: function (settings) {
const mail = settings.contactEmail || '';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In initialize, I think we should always set this.contactEmail to be equal to settings.contactEmail, with a default of null if it's not provided.

Then, in render, we should conditionally apply the concatenation with mailto: (encoding it if necessary), then pass that result (or null) into the template.

This will also LayoutFooterView to be subclassed such that it takes the same parameters, but renders them in a different way.

@@ -39,6 +39,7 @@ var App = View.extend({
initialize: function (settings) {
this._started = false;
settings = settings || {};
this.contactEmail = settings.contactEmail || '';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use null for the value when no setting is provided, since that will never be confused with a case where a server sends a setting value that's purposely set to empty string.

@@ -42,6 +42,9 @@ def testAccessWebRoot(self):
self.assertTrue('girder_app.min.js' in body)
self.assertTrue('girder_lib.min.js' in body)

emailAddress = self.model('setting').get(SettingKey.CONTACT_EMAIL_ADDRESS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just going to get the default.

To ensure that changes to this setting are immediately respected, we should next set this setting to something totally different, then make another request to get the webroot, and ensure that the value is updated.

@@ -1,6 +1,7 @@
.g-footer-links
a#g-about-link(href="http://girder.readthedocs.org/en/latest/user-guide.html") About
a#g-contact-link(href="mailto:kitware@kitware.com") Contact
if (contactHref !== '')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pug conditionals don't need parentheses.

I think you can check whether this is truthy at all (if contactHref), which will fail if it's null (intentionally not set) or an empty string (intentionally set, but empty).

input#g-core-contact-email-address.form-control.input-sm(
type="text", value=settings['core.contact_email_address'] || '',
placeholder=`Default: ${defaults['core.contact_email_address'] || 'none'}`,
title="The contact email address for support.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about title="The contact email address for users to obtain support with this instance."? I'm open to even better phrasing, but this is a little more descriptive.

@setting_utilities.validator(SettingKey.CONTACT_EMAIL_ADDRESS)
def validateCoreContactEmailAddress(doc):
if not doc['value']:
raise ValidationException('Email from address must not be blank.', 'value')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to address this.

initialize: function (settings) {
const mail = settings.contactEmail || '';
if (mail !== '') {
this.emailHref = 'mailto:' + mail;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use an ES6 template literal for this concatenation.

@Pierre-Assemat Pierre-Assemat force-pushed the set-email-contact-link branch 2 times, most recently from d75c5d3 to f74043a Compare August 14, 2017 18:26
@brianhelba
Copy link
Member

After reading more of RFC6068 myself, it's clear that we cannot use a name-addr (like 'Kitware Support <kitware@kitware.com>' or 'Foo Bar <foo@bar.com>'.

Although that spec also requires us to percent-encode many other characters that may appear in a valid email address (e.g. +), I think we can add the percent-encoding as part of a later enhancement. It will need to be done in the Javascript code, rather than the Python validation, as it's specific to the mailto: encoding, not the value of the email address itself. Some basic validation of the email address in the setting would also be nice, but apparently that's not trivial either.

So, for now, I think you need to just replace the default and test email addresses with just the core addr-spec components (e.g. 'kitware@kitware.com' or 'foo@bar.com').

@Pierre-Assemat
Copy link
Author

Pierre-Assemat commented Aug 15, 2017

Do I need to test if it's a good email address with some regular expression too ?

And do I need to change the Email from address too ?
screenshot from 2017-08-15 08-41-48

@brianhelba
Copy link
Member

EMAIL_FROM_ADDRESS is not used in a mailto: link, and so does not need to follow the stricter rules of RFC6068, rather, it can probably use RFC5322.

Because:

  • EMAIL_FROM_ADDRESS already does not validate using an email regex
  • There is the potential for duplicate functionality with the email_regex setting from Girder's config file
  • A robust regex for email-validation looks to be quite complicated

I think it's justified to merge this without extensive validation, then fix things more globally in a separate PR. I don't want to derail this entire PR for a corner case, as long as we eventually fix the corner case. I'll file an issue for it now.

@brianhelba
Copy link
Member

#2282

Copy link
Member

@brianhelba brianhelba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A minor issue inline.

More importantly, I realized that this now contains an injection vulnerability. Try setting the contact email address to exactly

foo@bar.com'});});</script><script>$(function(){girder.app.render(); alert('Pwned');});</script><script>$(function(){new girder.views.App({parentView: null,dummy: '

via the settings page.

While we could attempt to sanitize some of this via a more sophisticated validator, the simplest and most reliable way is to probably encode the string to JSON programatically, which will escape any quotes for us. Any evil script tags, etc. would remain unescaped inside the string, but they would be escaped when Pug renders the footer link (since Pug escapes everything by default).

So, rather than include quotes in the webroot.mako template, use Python's to convert the string value to JSON, and inject that result into webroot.mako, and set it (without explicit quotes) as the value of contactEmail: .

initialize: function (settings) {
const mail = settings.contactEmail || null;
if (mail !== null) {
this.emailHref = `mailto: ${mail}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RFC6068 says that there's no space between mailto: and the email address,

Also, in my opinion, it'd be cleaner overall to name this local variable const contactEmail, and the instance variable this.contactHref.

Copy link
Author

@Pierre-Assemat Pierre-Assemat Aug 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you propose to convert into JSON you mean in the webroot.py ? Just like that :
contactEmail = json.dumps(ModelImporter.model('setting').get(SettingKey.CONTACT_EMAIL_ADDRESS))

And I just insert this into the template webroot.mako ?

brianhelba
brianhelba previously approved these changes Aug 16, 2017
@Pierre-Assemat Pierre-Assemat merged commit 26341b3 into master Aug 16, 2017
@Pierre-Assemat Pierre-Assemat deleted the set-email-contact-link branch August 16, 2017 19:54
@mgrauer
Copy link
Contributor

mgrauer commented Aug 16, 2017

Two in one day! 🥂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants