8 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/109372878f62/ Changeset: 109372878f62 User: dannon Date: 2015-01-13 19:17:33+00:00 Summary: Initial password reset token feature. Affected #: 9 files diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c config/galaxy.ini.sample --- a/config/galaxy.ini.sample +++ b/config/galaxy.ini.sample @@ -354,10 +354,6 @@ # If no message specified the warning box will not be shown. #registration_warning_message = Please register only one account - we provide this service free of charge and have limited computational resources. Multi-accounts are tracked and will be subjected to account termination and data deletion. -# When users opt to reset passwords, new ones are created, this option -# specifies the length of these passwords. -#reset_password_length = 15 - # -- Account activation diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -161,7 +161,6 @@ self.job_walltime_delta = timedelta( 0, s, 0, 0, m, h ) self.admin_users = kwargs.get( "admin_users", "" ) self.admin_users_list = [u.strip() for u in self.admin_users.split(',') if u] - self.reset_password_length = int( kwargs.get('reset_password_length', '15') ) self.mailing_join_addr = kwargs.get('mailing_join_addr', 'galaxy-announce-join@bx.psu.edu') self.error_email_to = kwargs.get( 'error_email_to', None ) self.activation_email = kwargs.get( 'activation_email', None ) diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -18,6 +18,7 @@ import socket import time import numbers +from datetime import datetime, timedelta from uuid import UUID, uuid4 from string import Template from itertools import ifilter @@ -31,7 +32,7 @@ import galaxy.model.orm.now from galaxy.security import get_permitted_actions from galaxy.util import is_multi_byte, nice_size, Params, restore_text, send_mail -from galaxy.util import ready_name_for_url +from galaxy.util import ready_name_for_url, unique_id from galaxy.util.bunch import Bunch from galaxy.util.hash_util import new_secure_hash from galaxy.util.directory_hash import directory_hash_id @@ -261,6 +262,14 @@ environment = User.user_template_environment( user ) return Template( in_string ).safe_substitute( environment ) +class PasswordResetToken( object ): + def __init__( self, user, token=None): + if token: + self.token = token + else: + self.token = unique_id() + self.user = user + self.expiration_time = datetime.now() + timedelta(hours=24) class BaseJobMetric( object ): diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -68,6 +68,11 @@ Column( "provider", TrimmedString( 255 ) ), ) +model.PasswordResetToken.table = Table("password_reset_token", metadata, + Column( "token", String( 32 ), primary_key=True, unique=True, index=True ), + Column( "expiration_time", DateTime ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) ) + model.History.table = Table( "history", metadata, Column( "id", Integer, primary_key=True), Column( "create_time", DateTime, default=now ), @@ -1564,6 +1569,10 @@ api_keys=relation( model.APIKeys, backref="user", order_by=desc( model.APIKeys.table.c.create_time ) ), ) ) +mapper( model.PasswordResetToken, model.PasswordResetToken.table, + properties=dict( user=relation( model.User, backref="reset_tokens") ) ) + + # Set up proxy so that this syntax is possible: # <user_obj>.preferences[pref_name] = pref_value model.User.preferences = association_proxy('_preferences', 'value', creator=model.UserPreference) diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c lib/galaxy/model/migrate/versions/0126_password_reset.py --- /dev/null +++ b/lib/galaxy/model/migrate/versions/0126_password_reset.py @@ -0,0 +1,43 @@ +""" +Migration script for the password reset table +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from migrate import * +from migrate.changeset import * +from galaxy.model.custom_types import * + +import datetime +now = datetime.datetime.utcnow + +import logging +log = logging.getLogger( __name__ ) + +metadata = MetaData() + +PasswordResetToken_table = Table("password_reset_token", metadata, + Column( "token", String( 32 ), primary_key=True, unique=True, index=True ), + Column( "expiration_time", DateTime ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True )) + + +def upgrade(migrate_engine): + metadata.bind = migrate_engine + print __doc__ + metadata.reflect() + try: + PasswordResetToken_table.create() + except Exception as e: + print str(e) + log.exception("Creating %s table failed: %s" % (PasswordResetToken_table.name, str( e ) ) ) + + +def downgrade(migrate_engine): + metadata.bind = migrate_engine + metadata.reflect() + try: + PasswordResetToken_table.drop() + except Exception as e: + print str(e) + log.exception("Dropping %s table failed: %s" % (PasswordResetToken_table.name, str( e ) ) ) diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -7,7 +7,6 @@ import os import random import socket -import string import urllib from datetime import datetime, timedelta @@ -41,6 +40,13 @@ <p/> """ +PASSWORD_RESET_TEMPLATE = """ +To reset your Galaxy password for the instance at %s, use the following link: + + <a href="%s">%s</a> + +If you did not make this request, no action is necessary on your part, though +you may want to notify an administrator.""" class UserOpenIDGrid( grids.Grid ): use_panels = False @@ -1031,38 +1037,6 @@ trans.sa_session.add( user ) trans.sa_session.flush() message = 'The login information has been updated with the changes.' - elif user and params.get( 'change_password_button', False ): - # Editing password. Do not sanitize passwords, so get from kwd - # and not params (which were sanitized). - password = kwd.get( 'password', '' ) - confirm = kwd.get( 'confirm', '' ) - ok = True - if not is_admin: - # If the current user is changing their own password, validate their current password - current = kwd.get( 'current', '' ) - if not trans.user.check_password( current ): - message = 'Invalid current password' - status = 'error' - ok = False - if ok: - # Validate the new password - message = validate_password( trans, password, confirm ) - if message: - status = 'error' - else: - # Save new password - user.set_password_cleartext( password ) - # Invalidate all other sessions - for other_galaxy_session in trans.sa_session.query( trans.app.model.GalaxySession ) \ - .filter( and_( trans.app.model.GalaxySession.table.c.user_id == trans.user.id, - trans.app.model.GalaxySession.table.c.is_valid is True, - trans.app.model.GalaxySession.table.c.id != trans.galaxy_session.id ) ): - other_galaxy_session.is_valid = False - trans.sa_session.add( other_galaxy_session ) - trans.sa_session.add( user ) - trans.sa_session.flush() - trans.log_event( "User change password" ) - message = 'The password has been changed and any other existing Galaxy sessions have been logged out (but jobs in histories in those sessions will not be interrupted).' elif user and params.get( 'edit_user_info_button', False ): # Edit user information - webapp MUST BE 'galaxy' user_type_fd_id = params.get( 'user_type_fd_id', 'none' ) @@ -1105,38 +1079,99 @@ **kwd ) ) @web.expose - def reset_password( self, trans, email=None, **kwd ): + def change_password( self, trans, token=None, **kwd): + """ + Provides a form with which one can change their password. If token is + provided, don't require current password. + """ + status = None + message = None + user = None + if kwd.get( 'change_password_button', False ): + password = kwd.get( 'password', '' ) + confirm = kwd.get( 'confirm', '' ) + current = kwd.get( 'current', '' ) + token_result = None + if token: + # If a token was supplied, validate and set user + token_result = trans.sa_session.query( trans.app.model.PasswordResetToken ).get(token) + if token_result and token_result.expiration_time > datetime.now(): + user = token_result.user + else: + return trans.show_error_message("Invalid or expired password reset token, please request a new one.") + else: + # The user is changing their own password, validate their current password + if trans.user.check_password( current ): + user = trans.user + else: + message = 'Invalid current password' + status = 'error' + if user: + # Validate the new password + message = validate_password( trans, password, confirm ) + if message: + status = 'error' + else: + # Save new password + user.set_password_cleartext( password ) + # if we used a token, invalidate it and log the user in. + if token_result: + trans.handle_user_login(token_result.user) + token_result.expiration_time = datetime.now() + trans.sa_session.add(token_result) + # Invalidate all other sessions + for other_galaxy_session in trans.sa_session.query( trans.app.model.GalaxySession ) \ + .filter( and_( trans.app.model.GalaxySession.table.c.user_id == user.id, + trans.app.model.GalaxySession.table.c.is_valid is True, + trans.app.model.GalaxySession.table.c.id != trans.galaxy_session.id ) ): + other_galaxy_session.is_valid = False + trans.sa_session.add( other_galaxy_session ) + trans.sa_session.add( user ) + trans.sa_session.flush() + trans.log_event( "User change password" ) + return trans.show_ok_message('The password has been changed and any other existing Galaxy sessions have been logged out (but jobs in histories in those sessions will not be interrupted).') + return trans.fill_template( '/user/change_password.mako', + token=token, + status=status, + message=message ) + + + @web.expose + def reset_password( self, trans, email=None, token=None, **kwd ): """Reset the user's password. Send an email with the new password.""" if trans.app.config.smtp_server is None: - return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact your local Galaxy administrator." ) - message = '' - status = 'done' + return trans.show_error_message( "Mail is not configured for this" + "Galaxy instance and password reset information cannot be sent." + "Please contact your local Galaxy administrator." ) + message = None + status = None if kwd.get( 'reset_password_button', False ): reset_user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email == email ).first() user = trans.get_user() if reset_user: if user and user.id != reset_user.id: + # This doesn't make any sense because all they have to do is log + # out and then try it again #TODO revisit why this exists. message = "You may only reset your own password." status = 'error' else: - chars = string.letters + string.digits - new_pass = "" + prt = model.PasswordResetToken(reset_user) + trans.sa_session.add(prt) + trans.sa_session.flush() reset_password_length = getattr( trans.app.config, "reset_password_length", 15 ) - for i in range( reset_password_length ): - new_pass = new_pass + random.choice( chars ) host = trans.request.host.split( ':' )[ 0 ] if host == 'localhost': host = socket.getfqdn() - body = 'Your password on %s has been reset to:\n\n %s\n' % ( host, new_pass ) + reset_url = url_for( controller='user', action="change_password", token=prt.token, qualified=True) + body = PASSWORD_RESET_TEMPLATE % ( host, reset_url, reset_url ) frm = 'galaxy-no-reply@' + host subject = 'Galaxy Password Reset' try: util.send_mail( frm, email, subject, body, trans.app.config ) - reset_user.set_password_cleartext( new_pass ) trans.sa_session.add( reset_user ) trans.sa_session.flush() trans.log_event( "User reset password: %s" % email ) - message = "Password has been reset and emailed to: %s. <a href='%s'>Click here</a> to return to the login form." % ( escape( email ), web.url_for( controller='user', action='login', noredirect='true' ) ) + message = "An email has been sent to %s. Please refer to that email for more instructions." % ( escape( email ) ) except Exception, e: status = 'error' message = 'Failed to reset password: %s' % str( e ) diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c templates/user/change_password.mako --- /dev/null +++ b/templates/user/change_password.mako @@ -0,0 +1,31 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +%if message: + ${render_msg( message, status )} +%endif + +<div class="toolForm"> + <form name="change_password" id="change_password" action="${h.url_for( controller='user', action='change_password' )}" method="post" > + <div class="toolFormTitle">Change Password</div> + %if token: + <input type="hidden" name="token" value="${token|h}"/> + %elif not is_admin: + <div class="form-row"> + <label>Current password:</label> + <input type="password" name="current" value="" size="40"/> + </div> + %endif + <div class="form-row"> + <label>New password:</label> + <input type="password" name="password" value="" size="40"/> + </div> + <div class="form-row"> + <label>Confirm:</label> + <input type="password" name="confirm" value="" size="40"/> + </div> + <div class="form-row"> + <input type="submit" name="change_password_button" value="Save"/> + </div> + </form> +</div> diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c templates/user/index.mako --- a/templates/user/index.mako +++ b/templates/user/index.mako @@ -6,7 +6,8 @@ <ul> %if t.webapp.name == 'galaxy': %if not trans.app.config.use_remote_user: - <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a> (email, password, etc.)</li> + <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a> (email, address, etc.)</li> + <li><a href="${h.url_for( controller='user', action='change_password', cntrller=cntrller )}">${_('Change your password')}</a></li> %endif <li><a href="${h.url_for( controller='user', action='set_default_permissions', cntrller=cntrller )}">${_('Change default permissions')}</a> for new histories</li><li><a href="${h.url_for( controller='user', action='api_keys', cntrller=cntrller )}">${_('Manage your API keys')}</a></li> diff -r a2308bdc93b897af974766b190abe019ade49e9a -r 109372878f62317b74b57b47bb37d44b486ccf0c templates/user/info.mako --- a/templates/user/info.mako +++ b/templates/user/info.mako @@ -127,27 +127,4 @@ </div></form></div> - <p></p> - <div class="toolForm"> - <form name="change_password" id="change_password" action="${h.url_for( controller='user', action='edit_info', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" > - <div class="toolFormTitle">Change Password</div> - %if not is_admin: - <div class="form-row"> - <label>Current password:</label> - <input type="password" name="current" value="" size="40"/> - </div> - %endif - <div class="form-row"> - <label>New password:</label> - <input type="password" name="password" value="" size="40"/> - </div> - <div class="form-row"> - <label>Confirm:</label> - <input type="password" name="confirm" value="" size="40"/> - </div> - <div class="form-row"> - <input type="submit" name="change_password_button" value="Save"/> - </div> - </form> - </div></%def> https://bitbucket.org/galaxy/galaxy-central/commits/d0de709cc3e1/ Changeset: d0de709cc3e1 User: dannon Date: 2015-01-13 19:21:08+00:00 Summary: pep8, import dedupe in user controller. Affected #: 1 file diff -r 109372878f62317b74b57b47bb37d44b486ccf0c -r d0de709cc3e16ca6e0b9e28ee91ea124467081a4 lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -28,12 +28,10 @@ UsesFormDefinitionsMixin) from galaxy.web.form_builder import build_select_field, CheckboxField from galaxy.web.framework.helpers import escape, grids, time_ago -from galaxy.web.framework.helpers import time_ago, grids -from markupsafe import escape log = logging.getLogger( __name__ ) -require_login_template = """ +REQUIRE_LOGIN_TEMPLATE = """ <p> This %s has been configured such that only users who are logged in may use it.%s </p> @@ -48,6 +46,7 @@ If you did not make this request, no action is necessary on your part, though you may want to notify an administrator.""" + class UserOpenIDGrid( grids.Grid ): use_panels = False title = "OpenIDs linked to your account" @@ -483,14 +482,14 @@ create_account_str = " If you don't already have an account, <a href='%s'>you may create one</a>." % \ web.url_for( controller='user', action='create', cntrller='user' ) if trans.webapp.name == 'galaxy': - header = require_login_template % ( "Galaxy instance", create_account_str ) + header = REQUIRE_LOGIN_TEMPLATE % ( "Galaxy instance", create_account_str ) else: - header = require_login_template % ( "Galaxy tool shed", create_account_str ) + header = REQUIRE_LOGIN_TEMPLATE % ( "Galaxy tool shed", create_account_str ) else: if trans.webapp.name == 'galaxy': - header = require_login_template % ( "Galaxy instance", "" ) + header = REQUIRE_LOGIN_TEMPLATE % ( "Galaxy instance", "" ) else: - header = require_login_template % ( "Galaxy tool shed", "" ) + header = REQUIRE_LOGIN_TEMPLATE % ( "Galaxy tool shed", "" ) return trans.fill_template( '/user/login.mako', email=email, header=header, @@ -1135,14 +1134,13 @@ status=status, message=message ) - @web.expose def reset_password( self, trans, email=None, token=None, **kwd ): """Reset the user's password. Send an email with the new password.""" if trans.app.config.smtp_server is None: - return trans.show_error_message( "Mail is not configured for this" - "Galaxy instance and password reset information cannot be sent." - "Please contact your local Galaxy administrator." ) + return trans.show_error_message( "Mail is not configured for this Galaxy instance " + "and password reset information cannot be sent." + "Please contact your local Galaxy administrator." ) message = None status = None if kwd.get( 'reset_password_button', False ): https://bitbucket.org/galaxy/galaxy-central/commits/9fa4eee8e77c/ Changeset: 9fa4eee8e77c User: dannon Date: 2015-01-13 19:22:03+00:00 Summary: Remove unused old password_reset_length for good. Affected #: 1 file diff -r d0de709cc3e16ca6e0b9e28ee91ea124467081a4 -r 9fa4eee8e77c2605b123ca9f686dd1970a1bfd2a lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -1156,7 +1156,6 @@ prt = model.PasswordResetToken(reset_user) trans.sa_session.add(prt) trans.sa_session.flush() - reset_password_length = getattr( trans.app.config, "reset_password_length", 15 ) host = trans.request.host.split( ':' )[ 0 ] if host == 'localhost': host = socket.getfqdn() https://bitbucket.org/galaxy/galaxy-central/commits/80361f1c960f/ Changeset: 80361f1c960f User: dannon Date: 2015-01-13 19:24:15+00:00 Summary: Style tweaks in user_info.mako Affected #: 1 file diff -r 9fa4eee8e77c2605b123ca9f686dd1970a1bfd2a -r 80361f1c960fdbe48e83549f92c001d36d72a78d templates/user/info.mako --- a/templates/user/info.mako +++ b/templates/user/info.mako @@ -7,7 +7,7 @@ <script type="text/javascript"> $(document).ready(function() { - function validateString(test_string, type) { + function validateString(test_string, type) { var mail_re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var username_re = /^[a-z0-9\-]{4,255}$/; if (type === 'email') { @@ -15,7 +15,7 @@ } else if (type === 'username'){ return username_re.test(test_string); } - } + } function renderError(message) { $(".donemessage").hide(); @@ -26,8 +26,9 @@ div.className = "errormessage"; div.innerHTML = message; document.body.insertBefore( div, document.body.firstChild ); - } } + } + function renderDone(message) { $(".errormessage").hide(); if ($(".donemessage").length === 1) { @@ -37,8 +38,8 @@ div.className = "donemessage"; div.innerHTML = message; document.body.insertBefore( div, document.body.firstChild ); - } } + } original_email = $( '#email_input' ).val(); original_username = $( '#name_input' ).val(); @@ -55,7 +56,6 @@ var hidden_input = '<input type="hidden" id="login_info_button" name="login_info_button" value="Submit"/>'; $( '#send' ).attr( 'disabled', 'disabled' ); $( "#email_input" ).before( hidden_input ); - if ( original_email !== email ){ if ( email.length > 255 ){ renderError( error_text_email_long ); validForm = false; } else if ( !validateString( email, "email" ) ){ renderError( error_text_email ); validForm = false; } @@ -66,7 +66,7 @@ if ( nothing_changed ){ renderDone( "Nothing has changed." ); } - if ( !validForm || nothing_changed ) { + if ( !validForm || nothing_changed ) { e.preventDefault(); // reactivate the button if the form wasn't submitted $( '#send' ).removeAttr( 'disabled' ); https://bitbucket.org/galaxy/galaxy-central/commits/b64ae555ad12/ Changeset: b64ae555ad12 User: dannon Date: 2015-01-13 19:33:56+00:00 Summary: More minor style tweaks in user controller. Affected #: 1 file diff -r 80361f1c960fdbe48e83549f92c001d36d72a78d -r b64ae555ad12fa3fef9a96954b33f8f23b723893 lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -1153,13 +1153,15 @@ message = "You may only reset your own password." status = 'error' else: - prt = model.PasswordResetToken(reset_user) - trans.sa_session.add(prt) + prt = model.PasswordResetToken( reset_user ) + trans.sa_session.add( prt ) trans.sa_session.flush() host = trans.request.host.split( ':' )[ 0 ] if host == 'localhost': host = socket.getfqdn() - reset_url = url_for( controller='user', action="change_password", token=prt.token, qualified=True) + reset_url = url_for( controller='user', + action="change_password", + token=prt.token, qualified=True) body = PASSWORD_RESET_TEMPLATE % ( host, reset_url, reset_url ) frm = 'galaxy-no-reply@' + host subject = 'Galaxy Password Reset' https://bitbucket.org/galaxy/galaxy-central/commits/27fec2663c6c/ Changeset: 27fec2663c6c User: dannon Date: 2015-01-13 19:58:35+00:00 Summary: Default to a response that doesn't leak user information (whether or not the user exists, when resetting password. Affected #: 1 file diff -r b64ae555ad12fa3fef9a96954b33f8f23b723893 -r 27fec2663c6c9305926f6bc34ba074042dd467cb lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -1142,8 +1142,10 @@ "and password reset information cannot be sent." "Please contact your local Galaxy administrator." ) message = None - status = None + status = 'done' if kwd.get( 'reset_password_button', False ): + # Default to a non-userinfo-leaking response message + message = "Your reset request for %s has been received. Please check your email account for more instructions. If you do not receive an email shortly, please contact an administrator." % ( escape( email ) ) reset_user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email == email ).first() user = trans.get_user() if reset_user: @@ -1170,16 +1172,10 @@ trans.sa_session.add( reset_user ) trans.sa_session.flush() trans.log_event( "User reset password: %s" % email ) - message = "An email has been sent to %s. Please refer to that email for more instructions." % ( escape( email ) ) except Exception, e: status = 'error' message = 'Failed to reset password: %s' % str( e ) log.exception( 'Unable to reset password.' ) - elif email is not None: - message = "The specified user does not exist." - status = 'error' - elif email is None: - email = "" return trans.fill_template( '/user/reset_password.mako', message=message, status=status ) https://bitbucket.org/galaxy/galaxy-central/commits/5222b149d9dc/ Changeset: 5222b149d9dc User: dannon Date: 2015-01-13 20:08:55+00:00 Summary: Change password does not need 'cntrller'. Affected #: 1 file diff -r 27fec2663c6c9305926f6bc34ba074042dd467cb -r 5222b149d9dcdd84fe593e64e3b48c3c7a0bc395 templates/user/index.mako --- a/templates/user/index.mako +++ b/templates/user/index.mako @@ -7,7 +7,7 @@ %if t.webapp.name == 'galaxy': %if not trans.app.config.use_remote_user: <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a> (email, address, etc.)</li> - <li><a href="${h.url_for( controller='user', action='change_password', cntrller=cntrller )}">${_('Change your password')}</a></li> + <li><a href="${h.url_for( controller='user', action='change_password' )}">${_('Change your password')}</a></li> %endif <li><a href="${h.url_for( controller='user', action='set_default_permissions', cntrller=cntrller )}">${_('Change default permissions')}</a> for new histories</li><li><a href="${h.url_for( controller='user', action='api_keys', cntrller=cntrller )}">${_('Manage your API keys')}</a></li> https://bitbucket.org/galaxy/galaxy-central/commits/d2cafc77eee4/ Changeset: d2cafc77eee4 User: dannon Date: 2015-01-13 20:31:11+00:00 Summary: Correct spacing in mail configuration message, was missing one between sentences. Affected #: 1 file diff -r 5222b149d9dcdd84fe593e64e3b48c3c7a0bc395 -r d2cafc77eee48d07b1958e395f6a8cf58770cf94 lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -1139,7 +1139,7 @@ """Reset the user's password. Send an email with the new password.""" if trans.app.config.smtp_server is None: return trans.show_error_message( "Mail is not configured for this Galaxy instance " - "and password reset information cannot be sent." + "and password reset information cannot be sent. " "Please contact your local Galaxy administrator." ) message = None status = 'done' Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.