def create( self, trans, payload, **kwd ): """ POST /api/users Creates a new Galaxy user. """ if not trans.app.config.allow_user_creation and not trans.user_is_admin(): raise exceptions.ConfigDoesNotAllowException( 'User creation is not allowed in this Galaxy instance' ) if trans.app.config.use_remote_user and trans.user_is_admin(): user = trans.get_or_create_remote_user( remote_user_email=payload['remote_user_email'] ) elif trans.user_is_admin(): username = payload[ 'username' ] email = payload[ 'email' ] password = payload[ 'password' ] message = "\n".join( [ validate_email( trans, email ), validate_password( trans, password, password ), validate_publicname( trans, username ) ] ).rstrip() if message: raise exceptions.RequestParameterInvalidException( message ) else: user = self.create_user( trans=trans, email=email, username=username, password=password ) else: raise exceptions.NotImplemented() item = user.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id, 'total_disk_usage': float } ) return item
def send_reset_email(self, trans, payload={}, **kwd): """Reset the user's password. Send an email with token that allows a password change.""" if self.app.config.smtp_server is None: return "Mail is not configured for this Galaxy instance and password reset information cannot be sent. Please contact your local Galaxy administrator." email = payload.get("email") if not email: return "Please provide your email." message = validate_email(trans, email, check_dup=False) if message: return message else: reset_user, prt = self.get_reset_token(trans, email) if prt: host = self.__get_host(trans) reset_url = url_for(controller='root', action='login', token=prt.token) body = PASSWORD_RESET_TEMPLATE % ( host, prt.expiration_time.strftime( trans.app.config.pretty_datetime_format), trans.request.host, reset_url) frm = trans.app.config.email_from or 'galaxy-no-reply@' + host subject = 'Galaxy Password Reset' try: util.send_mail(frm, email, subject, body, self.app.config) trans.sa_session.add(reset_user) trans.sa_session.flush() trans.log_event('User reset password: %s' % email) except Exception as e: log.debug(body) return "Failed to submit email. Please contact the administrator: %s" % util.unicodify( e) else: return "Failed to produce password reset token. User not found."
def __validate(self, trans, email, password, confirm, username): if username in ['repos']: return "The term '%s' is a reserved word in the Tool Shed, so it cannot be used as a public user name." % username message = "\n".join([validate_email(trans, email), validate_password(trans, password, confirm), validate_publicname(trans, username)]).rstrip() return message
def __validate(self, trans, email, password, confirm, username): message = "\n".join([ validate_email(trans, email), validate_password(trans, password, confirm), validate_publicname(trans, username) ]).rstrip() return message
def create(self, trans: GalaxyWebTransaction, payload: dict, **kwd): """ POST /api/users Creates a new Galaxy user. """ if not trans.app.config.allow_user_creation and not trans.user_is_admin: raise exceptions.ConfigDoesNotAllowException( 'User creation is not allowed in this Galaxy instance') if trans.app.config.use_remote_user and trans.user_is_admin: user = trans.get_or_create_remote_user( remote_user_email=payload['remote_user_email']) elif trans.user_is_admin: username = payload['username'] email = payload['email'] password = payload['password'] message = "\n".join((validate_email(trans, email), validate_password(trans, password, password), validate_publicname(trans, username))).rstrip() if message: raise exceptions.RequestParameterInvalidException(message) else: user = self.user_manager.create(email=email, username=username, password=password) else: raise exceptions.NotImplemented() item = user.to_dict(view='element', value_mapper={ 'id': trans.security.encode_id, 'total_disk_usage': float }) return item
def __autoregistration(self, trans, login, password): """ Does the autoregistration if enabled. Returns a message """ try: autoreg = trans.app.auth_manager.check_auto_registration(trans, login, password) except Conflict as conflict: return "Auto-registration failed, {}".format(conflict), None user = None if autoreg["auto_reg"]: email = autoreg["email"] username = autoreg["username"] message = " ".join([validate_email(trans, email, allow_empty=True), validate_publicname(trans, username)]).rstrip() if not message: user = self.user_manager.create(email=email, username=username, password="") if trans.app.config.user_activation_on: self.user_manager.send_activation_email(trans, email, username) # The handle_user_login() method has a call to the history_set_default_permissions() method # (needed when logging in with a history), user needs to have default permissions set before logging in if not trans.user_is_admin: trans.handle_user_login(user) trans.log_event("User (auto) created a new account") trans.log_event("User logged in") if "attributes" in autoreg and "roles" in autoreg["attributes"]: self.__handle_role_and_group_auto_creation( trans, user, autoreg["attributes"]["roles"], auto_create_groups=autoreg["auto_create_groups"], auto_create_roles=autoreg["auto_create_roles"], auto_assign_roles_to_groups_only=autoreg["auto_assign_roles_to_groups_only"]) else: message = "Auto-registration failed, contact your local Galaxy administrator. %s" % message else: message = "No such user or invalid password." return message, user
def register(self, trans, email=None, username=None, password=None, confirm=None, subscribe=False): """ Register a new user. """ if not trans.app.config.allow_user_creation and not trans.user_is_admin: message = "User registration is disabled. Please contact your local Galaxy administrator for an account." if trans.app.config.error_email_to is not None: message += " Contact: %s" % trans.app.config.error_email_to return None, message if not email or not username or not password or not confirm: return None, "Please provide email, username and password." message = "\n".join([validate_email(trans, email), validate_password(trans, password, confirm), validate_publicname(trans, username)]).rstrip() if message: return None, message email = util.restore_text(email) username = util.restore_text(username) message, status = trans.app.auth_manager.check_registration_allowed(email, username, password) if message: return None, message if subscribe: message = self.send_subscription_email(email) if message: return None, message user = self.create(email=email, username=username, password=password) if self.app.config.user_activation_on: self.send_activation_email(trans, email, username) return user, None
def reset_password(self, trans, email=None, **kwd): """Reset the user's password. Send an email with token that allows a password change.""" 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.") message = None status = 'done' if kwd.get('reset_password_button', False): message = validate_email(trans, email, check_dup=False) if not message: # 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() if not reset_user: # Perform a case-insensitive check only if the user wasn't found reset_user = trans.sa_session.query( trans.app.model.User).filter( func.lower(trans.app.model.User.table.c.email) == func.lower(email)).first() if reset_user: prt = trans.app.model.PasswordResetToken(reset_user) trans.sa_session.add(prt) trans.sa_session.flush() host = trans.request.host.split(':')[0] if host in ['localhost', '127.0.0.1', '0.0.0.0']: host = socket.getfqdn() reset_url = url_for(controller='user', action="change_password", token=prt.token, qualified=True) body = PASSWORD_RESET_TEMPLATE % ( host, prt.expiration_time.strftime( trans.app.config.pretty_datetime_format), reset_url) frm = trans.app.config.email_from or 'galaxy-no-reply@' + host subject = 'Galaxy Password Reset' try: util.send_mail(frm, email, subject, body, trans.app.config) trans.sa_session.add(reset_user) trans.sa_session.flush() trans.log_event("User reset password: %s" % email) except Exception: log.exception('Unable to reset password.') return trans.fill_template( '/webapps/tool_shed/user/reset_password.mako', message=message, status=status)
def __validate( self, trans, email, password, confirm, username ): if not username: return "A public user name is required in the Tool Shed." if username in [ 'repos' ]: return "The term <b>%s</b> is a reserved word in the Tool Shed, so it cannot be used as a public user name." % username message = validate_email( trans, email ) if not message: message = validate_password( trans, password, confirm ) if not message and username: message = validate_publicname( trans, username ) return message
def set_information(self, trans, id, payload={}, **kwd): """ PUT /api/users/{id}/information/inputs Save a user's email, username, addresses etc. :param id: the encoded id of the user :type id: str :param payload: data with new settings :type payload: dict """ user = self._get_user(trans, id) # Update email if 'email' in payload: email = payload.get('email') message = self._validate_email(email) or validate_email( trans, email, user) if message: raise exceptions.RequestParameterInvalidException(message) if user.email != email: # Update user email and user's private role name which must match private_role = trans.app.security_agent.get_private_user_role( user) private_role.name = email private_role.description = 'Private role for ' + email user.email = email trans.sa_session.add(user) trans.sa_session.add(private_role) trans.sa_session.flush() if trans.app.config.user_activation_on: # Deactivate the user if email was changed and activation is on. user.active = False if self.user_manager.send_activation_email( trans, user.email, user.username): message = 'The login information has been updated with the changes.<br>Verification email has been sent to your new email address. Please verify it by clicking the activation link in the email.<br>Please check your spam/trash folder in case you cannot find the message.' else: message = 'Unable to send activation email, please contact your local Galaxy administrator.' if trans.app.config.error_email_to is not None: message += ' Contact: %s' % trans.app.config.error_email_to raise exceptions.InternalServerError(message) # Update public name if 'username' in payload: username = payload.get('username') message = self._validate_publicname( username) or validate_publicname(trans, username, user) if message: raise exceptions.RequestParameterInvalidException(message) if user.username != username: user.username = username # Update user custom form user_info_form_id = payload.get('info|form_id') if user_info_form_id: prefix = 'info|' user_info_form = trans.sa_session.query( trans.app.model.FormDefinition).get( trans.security.decode_id(user_info_form_id)) user_info_values = {} for item in payload: if item.startswith(prefix): user_info_values[item[len(prefix):]] = payload[item] form_values = trans.model.FormValues(user_info_form, user_info_values) trans.sa_session.add(form_values) user.values = form_values # Update values for extra user preference items extra_user_pref_data = dict() extra_pref_keys = self._get_extra_user_preferences(trans) if extra_pref_keys is not None: for key in extra_pref_keys: key_prefix = key + '|' for item in payload: if item.startswith(key_prefix): # Show error message if the required field is empty if payload[item] == "": # Raise an exception when a required field is empty while saving the form keys = item.split("|") section = extra_pref_keys[keys[0]] for input in section['inputs']: if input['name'] == keys[1] and input[ 'required']: raise exceptions.ObjectAttributeMissingException( "Please fill the required field") extra_user_pref_data[item] = payload[item] user.preferences["extra_user_preferences"] = json.dumps( extra_user_pref_data) # Update user addresses address_dicts = {} address_count = 0 for item in payload: match = re.match(r'^address_(?P<index>\d+)\|(?P<attribute>\S+)', item) if match: groups = match.groupdict() index = int(groups['index']) attribute = groups['attribute'] address_dicts[index] = address_dicts.get(index) or {} address_dicts[index][attribute] = payload[item] address_count = max(address_count, index + 1) user.addresses = [] for index in range(0, address_count): d = address_dicts[index] if d.get('id'): try: user_address = trans.sa_session.query( trans.app.model.UserAddress).get( trans.security.decode_id(d['id'])) except Exception as e: raise exceptions.ObjectNotFound( 'Failed to access user address ({}). {}'.format( d['id'], e)) else: user_address = trans.model.UserAddress() trans.log_event('User address added') for field in AddressField.fields(): if str(field[2]).lower() == 'required' and not d.get(field[0]): raise exceptions.ObjectAttributeMissingException( 'Address {}: {} ({}) required.'.format( index + 1, field[1], field[0])) setattr(user_address, field[0], str(d.get(field[0], ''))) user_address.user = user user.addresses.append(user_address) trans.sa_session.add(user_address) trans.sa_session.add(user) trans.sa_session.flush() trans.log_event('User information added') return {'message': 'User information has been saved.'}
def edit_info( self, trans, cntrller, **kwd ): """ Edit user information = username, email or password. """ params = util.Params( kwd ) is_admin = cntrller == 'admin' and trans.user_is_admin() message = util.restore_text( params.get( 'message', '' ) ) status = params.get( 'status', 'done' ) user_id = params.get( 'user_id', None ) if user_id and is_admin: user = trans.sa_session.query( trans.app.model.User ).get( trans.security.decode_id( user_id ) ) elif user_id and ( not trans.user or trans.user.id != trans.security.decode_id( user_id ) ): message = 'Invalid user id' status = 'error' user = None else: user = trans.user if user and params.get( 'login_info_button', False ): # Editing email and username email = util.restore_text( params.get( 'email', '' ) ) username = util.restore_text( params.get( 'username', '' ) ).lower() # Validate the new values for email and username message = validate_email( trans, email, user ) if not message and username: message = validate_publicname( trans, username, user ) if message: status = 'error' else: if ( user.email != email ): # The user's private role name must match the user's login ( email ) private_role = trans.app.security_agent.get_private_user_role( user ) private_role.name = email private_role.description = 'Private role for ' + email # Change the email itself user.email = email trans.sa_session.add_all( ( user, private_role ) ) trans.sa_session.flush() if trans.webapp.name == 'galaxy' and trans.app.config.user_activation_on: user.active = False trans.sa_session.add( user ) trans.sa_session.flush() is_activation_sent = self.send_verification_email( trans, user.email, user.username ) if is_activation_sent: message = 'The login information has been updated with the changes.<br>Verification email has been sent to your new email address. Please verify it by clicking the activation link in the email.<br>Please check your spam/trash folder in case you cannot find the message.' else: message = 'Unable to send activation email, please contact your local Galaxy administrator.' if trans.app.config.error_email_to is not None: message += ' Contact: %s' % trans.app.config.error_email_to if ( user.username != username ): user.username = username trans.sa_session.add( user ) trans.sa_session.flush() message = 'The login information has been updated with the changes.' 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' ) if user_type_fd_id not in [ 'none' ]: user_type_form_definition = trans.sa_session.query( trans.app.model.FormDefinition ).get( trans.security.decode_id( user_type_fd_id ) ) elif user.values: user_type_form_definition = user.values.form_definition else: # User was created before any of the user_info forms were created user_type_form_definition = None if user_type_form_definition: values = self.get_form_values( trans, user, user_type_form_definition, **kwd ) else: values = {} flush_needed = False if user.values: # Editing the user info of an existing user with existing user info user.values.content = values trans.sa_session.add( user.values ) flush_needed = True elif values: form_values = trans.model.FormValues( user_type_form_definition, values ) trans.sa_session.add( form_values ) user.values = form_values flush_needed = True if flush_needed: trans.sa_session.add( user ) trans.sa_session.flush() message = "The user information has been updated with the changes." if user and trans.webapp.name == 'galaxy' and is_admin: kwd[ 'user_id' ] = trans.security.encode_id( user.id ) kwd[ 'id' ] = user_id if message: kwd[ 'message' ] = util.sanitize_text( message ) if status: kwd[ 'status' ] = status return trans.response.send_redirect( web.url_for( controller='user', action='manage_user_info', cntrller=cntrller, **kwd ) )
def set_information(self, trans, id, payload={}, **kwd): """ POST /api/users/{id}/information Save a user's email, username, addresses etc. :param id: the encoded id of the user :type id: str :param payload: data with new settings :type payload: dict """ user = self._get_user(trans, id) email = payload.get('email') username = payload.get('username') if email or username: message = self._validate_email_publicname( email, username) or validate_email(trans, email, user) if not message and username: message = validate_publicname(trans, username, user) if message: raise MessageException(message) if user.email != email: # Update user email and user's private role name which must match private_role = trans.app.security_agent.get_private_user_role( user) private_role.name = email private_role.description = 'Private role for ' + email user.email = email trans.sa_session.add(user) trans.sa_session.add(private_role) trans.sa_session.flush() if trans.app.config.user_activation_on: # Deactivate the user if email was changed and activation is on. user.active = False if self.send_verification_email(trans, user.email, user.username): message = 'The login information has been updated with the changes.<br>Verification email has been sent to your new email address. Please verify it by clicking the activation link in the email.<br>Please check your spam/trash folder in case you cannot find the message.' else: message = 'Unable to send activation email, please contact your local Galaxy administrator.' if trans.app.config.error_email_to is not None: message += ' Contact: %s' % trans.app.config.error_email_to raise MessageException(message) if user.username != username: # Update public name user.username = username # Update user custom form user_info_form_id = payload.get('info|form_id') if user_info_form_id: prefix = 'info|' user_info_form = trans.sa_session.query( trans.app.model.FormDefinition).get( trans.security.decode_id(user_info_form_id)) user_info_values = {} for item in payload: if item.startswith(prefix): user_info_values[item[len(prefix):]] = payload[item] form_values = trans.model.FormValues(user_info_form, user_info_values) trans.sa_session.add(form_values) user.values = form_values # Update user addresses address_dicts = {} address_count = 0 for item in payload: match = re.match(r'^address_(?P<index>\d+)\|(?P<attribute>\S+)', item) if match: groups = match.groupdict() index = int(groups['index']) attribute = groups['attribute'] address_dicts[index] = address_dicts.get(index) or {} address_dicts[index][attribute] = payload[item] address_count = max(address_count, index + 1) user.addresses = [] for index in range(0, address_count): d = address_dicts[index] if d.get('id'): try: user_address = trans.sa_session.query( trans.app.model.UserAddress).get( trans.security.decode_id(d['id'])) except Exception as e: raise MessageException( 'Failed to access user address (%s). %s' % (d['id'], e)) else: user_address = trans.model.UserAddress() trans.log_event('User address added') for field in AddressField.fields(): if str(field[2]).lower() == 'required' and not d.get(field[0]): raise MessageException('Address %s: %s (%s) required.' % (index + 1, field[1], field[0])) setattr(user_address, field[0], str(d.get(field[0], ''))) user_address.user = user user.addresses.append(user_address) trans.sa_session.add(user_address) trans.sa_session.add(user) trans.sa_session.flush() trans.log_event('User information added') return {'message': 'User information has been saved.'}
def edit_info(self, trans, cntrller, **kwd): """ Edit user information = username, email or password. """ params = util.Params(kwd) is_admin = cntrller == 'admin' and trans.user_is_admin() message = util.restore_text(params.get('message', '')) status = params.get('status', 'done') user_id = params.get('user_id', None) if user_id and is_admin: user = trans.sa_session.query(trans.app.model.User).get( trans.security.decode_id(user_id)) elif user_id and (not trans.user or trans.user.id != trans.security.decode_id(user_id)): message = 'Invalid user id' status = 'error' user = None else: user = trans.user if user and params.get('login_info_button', False): # Editing email and username email = util.restore_text(params.get('email', '')) username = util.restore_text(params.get('username', '')).lower() # Validate the new values for email and username message = validate_email(trans, email, user) if not message and username: message = validate_publicname(trans, username, user) if message: status = 'error' else: if (user.email != email): # The user's private role name must match the user's login ( email ) private_role = trans.app.security_agent.get_private_user_role( user) private_role.name = email private_role.description = 'Private role for ' + email # Change the email itself user.email = email trans.sa_session.add_all((user, private_role)) trans.sa_session.flush() if trans.webapp.name == 'galaxy' and trans.app.config.user_activation_on: user.active = False trans.sa_session.add(user) trans.sa_session.flush() is_activation_sent = self.send_verification_email( trans, user.email, user.username) if is_activation_sent: message = 'The login information has been updated with the changes.<br>Verification email has been sent to your new email address. Please verify it by clicking the activation link in the email.<br>Please check your spam/trash folder in case you cannot find the message.' else: message = 'Unable to send activation email, please contact your local Galaxy administrator.' if trans.app.config.error_email_to is not None: message += ' Contact: %s' % trans.app.config.error_email_to if (user.username != username): user.username = username trans.sa_session.add(user) trans.sa_session.flush() message = 'The login information has been updated with the changes.' 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') if user_type_fd_id not in ['none']: user_type_form_definition = trans.sa_session.query( trans.app.model.FormDefinition).get( trans.security.decode_id(user_type_fd_id)) elif user.values: user_type_form_definition = user.values.form_definition else: # User was created before any of the user_info forms were created user_type_form_definition = None if user_type_form_definition: values = self.get_form_values(trans, user, user_type_form_definition, **kwd) else: values = {} flush_needed = False if user.values: # Editing the user info of an existing user with existing user info user.values.content = values trans.sa_session.add(user.values) flush_needed = True elif values: form_values = trans.model.FormValues(user_type_form_definition, values) trans.sa_session.add(form_values) user.values = form_values flush_needed = True if flush_needed: trans.sa_session.add(user) trans.sa_session.flush() message = "The user information has been updated with the changes." if user and trans.webapp.name == 'galaxy' and is_admin: kwd['user_id'] = trans.security.encode_id(user.id) kwd['id'] = user_id if message: kwd['message'] = util.sanitize_text(message) if status: kwd['status'] = status return trans.response.send_redirect( web.url_for(controller='user', action='manage_user_info', cntrller=cntrller, **kwd))
def set_information(self, trans, id, payload={}, **kwd): """ POST /api/users/{id}/information Save a user's email, username, addresses etc. :param id: the encoded id of the user :type id: str :param payload: data with new settings :type payload: dict """ user = self._get_user(trans, id) email = payload.get('email') username = payload.get('username') if email or username: message = self._validate_email_publicname(email, username) or validate_email(trans, email, user) if not message and username: message = validate_publicname(trans, username, user) if message: raise MessageException(message) if user.email != email: # Update user email and user's private role name which must match private_role = trans.app.security_agent.get_private_user_role(user) private_role.name = email private_role.description = 'Private role for ' + email user.email = email trans.sa_session.add(user) trans.sa_session.add(private_role) trans.sa_session.flush() if trans.app.config.user_activation_on: # Deactivate the user if email was changed and activation is on. user.active = False if self.send_verification_email(trans, user.email, user.username): message = 'The login information has been updated with the changes.<br>Verification email has been sent to your new email address. Please verify it by clicking the activation link in the email.<br>Please check your spam/trash folder in case you cannot find the message.' else: message = 'Unable to send activation email, please contact your local Galaxy administrator.' if trans.app.config.error_email_to is not None: message += ' Contact: %s' % trans.app.config.error_email_to raise MessageException(message) if user.username != username: # Update public name user.username = username # Update user custom form user_info_form_id = payload.get('info|form_id') if user_info_form_id: prefix = 'info|' user_info_form = trans.sa_session.query(trans.app.model.FormDefinition).get(trans.security.decode_id(user_info_form_id)) user_info_values = {} for item in payload: if item.startswith(prefix): user_info_values[item[len(prefix):]] = payload[item] form_values = trans.model.FormValues(user_info_form, user_info_values) trans.sa_session.add(form_values) user.values = form_values # Update values for extra user preference items extra_user_pref_data = dict() get_extra_pref_keys = self._get_extra_user_preferences(trans) if get_extra_pref_keys is not None: for key in get_extra_pref_keys: key_prefix = key + '|' for item in payload: if item.startswith(key_prefix): # Show error message if the required field is empty if payload[item] == "": # Raise an exception when a required field is empty while saving the form keys = item.split("|") section = get_extra_pref_keys[keys[0]] for input in section['inputs']: if input['name'] == keys[1] and input['required']: raise MessageException("Please fill the required field") extra_user_pref_data[item] = payload[item] user.preferences["extra_user_preferences"] = json.dumps(extra_user_pref_data) # Update user addresses address_dicts = {} address_count = 0 for item in payload: match = re.match(r'^address_(?P<index>\d+)\|(?P<attribute>\S+)', item) if match: groups = match.groupdict() index = int(groups['index']) attribute = groups['attribute'] address_dicts[index] = address_dicts.get(index) or {} address_dicts[index][attribute] = payload[item] address_count = max(address_count, index + 1) user.addresses = [] for index in range(0, address_count): d = address_dicts[index] if d.get('id'): try: user_address = trans.sa_session.query(trans.app.model.UserAddress).get(trans.security.decode_id(d['id'])) except Exception as e: raise MessageException('Failed to access user address (%s). %s' % (d['id'], e)) else: user_address = trans.model.UserAddress() trans.log_event('User address added') for field in AddressField.fields(): if str(field[2]).lower() == 'required' and not d.get(field[0]): raise MessageException('Address %s: %s (%s) required.' % (index + 1, field[1], field[0])) setattr(user_address, field[0], str(d.get(field[0], ''))) user_address.user = user user.addresses.append(user_address) trans.sa_session.add(user_address) trans.sa_session.add(user) trans.sa_session.flush() trans.log_event('User information added') return {'message': 'User information has been saved.'}