def check_password_PBKDF2( guess, hashed ): # Split the database representation to extract cost_factor and salt name, hash_function, cost_factor, salt, encoded_original = hashed.split( '$', 5 ) # Hash the guess using the same parameters hashed_guess = pbkdf2_bin( bytes( guess ), salt, int( cost_factor ), KEY_LENGTH, getattr( hashlib, hash_function ) ) encoded_guess = b64encode( hashed_guess ) return safe_str_cmp( encoded_original, encoded_guess )
def check_password_PBKDF2(guess, hashed): # Split the database representation to extract cost_factor and salt name, hash_function, cost_factor, salt, encoded_original = hashed.split('$', 5) # Hash the guess using the same parameters hashed_guess = pbkdf2_bin(bytes(guess), salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function)) encoded_guess = b64encode(hashed_guess) return safe_str_cmp(encoded_original, encoded_guess)
def _check_master_api_key(self, api_key): master_api_key = getattr(self.app.config, 'master_api_key', None) if not master_api_key: return False # Hash keys to make them the same size, so we can do safe comparison. master_hash = hashlib.sha256(master_api_key).hexdigest() provided_hash = hashlib.sha256(api_key).hexdigest() return safe_str_cmp(master_hash, provided_hash)
def _check_master_api_key( self, api_key ): master_api_key = getattr( self.app.config, 'master_api_key', None ) if not master_api_key: return False # Hash keys to make them the same size, so we can do safe comparison. master_hash = hashlib.sha256( master_api_key ).hexdigest() provided_hash = hashlib.sha256( api_key ).hexdigest() return safe_str_cmp( master_hash, provided_hash )
def check_password(guess, hashed): """ Check a hashed password. Supports either PBKDF2 if the hash is prefixed with that string, or sha1 otherwise. """ if hashed.startswith("PBKDF2"): if check_password_PBKDF2(guess, hashed): return True else: # Passwords were originally encoded with sha1 and hexed if safe_str_cmp(hashlib.sha1(guess).hexdigest(), hashed): return True # Password does not match return False
def check_password(guess, hashed): """ Check a hashed password. Supports either PBKDF2 if the hash is prefixed with that string, or sha1 otherwise. """ if hashed.startswith("PBKDF2"): if check_password_PBKDF2(guess, hashed): return True else: # Passwords were originally encoded with sha1 and hexed if safe_str_cmp(hashlib.sha1(smart_str(guess)).hexdigest(), hashed): return True # Password does not match return False
def check_password_PBKDF2(guess, hashed): # Split the database representation to extract cost_factor and salt name, hash_function, cost_factor, salt, encoded_original = hashed.split('$', 5) if six.PY3: guess = bytes(guess, 'utf-8') salt = bytes(salt, 'utf-8') else: guess = smart_str(guess) hashed_guess = pbkdf2_bin(guess, salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function)) # Hash the guess using the same parameters hashed_guess = pbkdf2_bin(smart_str(guess), salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function)) encoded_guess = b64encode(hashed_guess) if six.PY3: encoded_guess = encoded_guess.decode('utf-8') return safe_str_cmp(encoded_original, encoded_guess)
def __authorize_job_access(self, trans, encoded_job_id, **kwargs): for key in [ "path", "job_key" ]: if key not in kwargs: error_message = "Job files action requires a valid '%s'." % key raise exceptions.ObjectAttributeMissingException( error_message ) job_id = trans.security.decode_id( encoded_job_id ) job_key = trans.security.encode_id( job_id, kind="jobs_files" ) if not util.safe_str_cmp( kwargs[ "job_key" ], job_key ): raise exceptions.ItemAccessibilityException("Invalid job_key supplied.") # Verify job is active. Don't update the contents of complete jobs. job = trans.sa_session.query( model.Job ).get( job_id ) if job.finished: error_message = "Attempting to read or modify the files of a job that has already completed." raise exceptions.ItemAccessibilityException( error_message ) return job
def __authorize_job_access(self, trans, encoded_job_id, **kwargs): for key in ["path", "job_key"]: if key not in kwargs: error_message = f"Job files action requires a valid '{key}'." raise exceptions.ObjectAttributeMissingException(error_message) job_id = trans.security.decode_id(encoded_job_id) job_key = trans.security.encode_id(job_id, kind="jobs_files") if not util.safe_str_cmp(kwargs["job_key"], job_key): raise exceptions.ItemAccessibilityException("Invalid job_key supplied.") # Verify job is active. Don't update the contents of complete jobs. job = trans.sa_session.query(model.Job).get(job_id) if job.finished: error_message = "Attempting to read or modify the files of a job that has already completed." raise exceptions.ItemAccessibilityException(error_message) return job
def __authorize_job_access(self, encoded_job_id, **kwargs): key = "job_key" if key not in kwargs: error_message = "Job files action requires a valid '%s'." % key raise exceptions.ObjectAttributeMissingException(error_message) job_id = self._security.decode_id(encoded_job_id) job_key = self._security.encode_id(job_id, kind="jobs_files") if not util.safe_str_cmp(kwargs["job_key"], job_key): raise exceptions.ItemAccessibilityException("Invalid job_key supplied.") # Verify job is active. Don't update the contents of complete jobs. sa_session = self._app.model.context.current job = sa_session.query(model.Job).get(job_id) if not job.running: error_message = "Attempting to read or modify the files of a job that has already completed." raise exceptions.ItemAccessibilityException(error_message) return job
def __call__(self, environ, start_response): # Allow display servers if self.display_servers and 'REMOTE_ADDR' in environ: try: host = socket.gethostbyaddr(environ['REMOTE_ADDR'])[0] except (socket.error, socket.herror, socket.gaierror, socket.timeout): # in the event of a lookup failure, deny access host = None if host in self.display_servers: environ[ self.remote_user_header] = 'remote_display_server@%s' % ( self.maildomain or 'example.org') return self.app(environ, start_response) # Apache sets REMOTE_USER to the string '(null)' when using the # Rewrite* method for passing REMOTE_USER and a user is # un-authenticated. Any other possible values need to go here as well. path_info = environ.get('PATH_INFO', '') # If the secret header is enabled, we expect upstream to send along some key # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value # # This is not an ideal location for this function. The reason being # that because this check is done BEFORE the REMOTE_USER check, it is # possible to attack the GX_SECRET key without having correct # credentials. However, that's why it's not "ideal", but it is "good # enough". The only users able to exploit this are ones with access to # the local system (unless Galaxy is listening on 0.0.0.0....). It # seems improbable that an attacker with access to the server hosting # Galaxy would not have access to Galaxy itself, and be attempting to # attack the system if self.config_secret_header is not None: if environ.get('HTTP_GX_SECRET') is None: title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but no shared secret key was provided by the upstream (proxy) server.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_secret</code> and <code>GX_SECRET</code> header must be set before you may access Galaxy. """ return self.error(start_response, title, message) if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header): title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but an incorrect shared secret key was provided by the upstream (proxy) server.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_secret</code> and <code>GX_SECRET</code> header must be set before you may access Galaxy. """ return self.error(start_response, title, message) if not environ.get(self.remote_user_header, '(null)').startswith('(null)'): if not environ[self.remote_user_header].count('@'): if self.maildomain is not None: environ[self.remote_user_header] += '@' + self.maildomain else: title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but only a username (not an email address) was provided by the upstream (proxy) server. Since Galaxy usernames are email addresses, a default mail domain must be set.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_maildomain</code> must be set before you may access Galaxy. """ return self.error(start_response, title, message) user_accessible_paths = ( '/user/api_keys', '/user/edit_username', '/user/dbkeys', '/user/toolbox_filters', '/user/set_default_permissions', ) admin_accessible_paths = ( '/user/create', '/user/logout', '/user/manage_user_info', '/user/edit_info', '/userskeys/all_users', ) if not path_info.startswith('/user'): # shortcut the following whitelist for non-user-controller # requests. pass elif environ[self.remote_user_header] in self.admin_users and \ any([path_info.startswith(prefix) for prefix in admin_accessible_paths]): # If the user is an admin user, and any of the admin accessible paths match..., allow them to execute that action. pass elif any([ path_info.startswith(prefix) for prefix in user_accessible_paths ]): # If the user is allowed to access the path, pass pass elif path_info == '/user' or path_info == '/user/': pass # We do allow access to the root user preferences page. elif path_info.startswith('/user'): # Any other endpoint in the user controller is off limits title = "Access to Galaxy user controls is disabled" message = """ User controls are disabled when Galaxy is configured for external authentication. """ return self.error(start_response, title, message) return self.app(environ, start_response) elif path_info.startswith('/api/'): # The API handles its own authentication via keys return self.app(environ, start_response) else: log.debug("Unable to identify user. %s not found" % self.remote_user_header) for k, v in environ.iteritems(): log.debug("%s = %s", k, v) title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but a username was not provided by the upstream (proxy) server. This is generally due to a misconfiguration in the upstream server.</p> <p>Please contact your local Galaxy administrator. """ return self.error(start_response, title, message)
def __call__( self, environ, start_response ): # Allow display servers if self.display_servers and 'REMOTE_ADDR' in environ: try: host = socket.gethostbyaddr( environ[ 'REMOTE_ADDR' ] )[0] except( socket.error, socket.herror, socket.gaierror, socket.timeout ): # in the event of a lookup failure, deny access host = None if host in self.display_servers: environ[ self.remote_user_header ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' ) return self.app( environ, start_response ) # Apache sets REMOTE_USER to the string '(null)' when using the # Rewrite* method for passing REMOTE_USER and a user is # un-authenticated. Any other possible values need to go here as well. path_info = environ.get('PATH_INFO', '') # If the secret header is enabled, we expect upstream to send along some key # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value # # This is not an ideal location for this function. The reason being # that because this check is done BEFORE the REMOTE_USER check, it is # possible to attack the GX_SECRET key without having correct # credentials. However, that's why it's not "ideal", but it is "good # enough". The only users able to exploit this are ones with access to # the local system (unless Galaxy is listening on 0.0.0.0....). It # seems improbable that an attacker with access to the server hosting # Galaxy would not have access to Galaxy itself, and be attempting to # attack the system if path_info.startswith( '/api/' ): # The API handles its own authentication via keys # Check for API key before checking for header return self.app( environ, start_response ) elif self.config_secret_header is not None: if environ.get('HTTP_GX_SECRET') is None: title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but no shared secret key was provided by the upstream (proxy) server.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_secret</code> and <code>GX_SECRET</code> header must be set before you may access Galaxy. """ return self.error( start_response, title, message ) if not safe_str_cmp(environ.get('HTTP_GX_SECRET', ''), self.config_secret_header): title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but an incorrect shared secret key was provided by the upstream (proxy) server.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_secret</code> and <code>GX_SECRET</code> header must be set before you may access Galaxy. """ return self.error( start_response, title, message ) if not environ.get(self.remote_user_header, '(null)').startswith('(null)'): if not environ[ self.remote_user_header ].count( '@' ): if self.maildomain is not None: environ[ self.remote_user_header ] += '@' + self.maildomain else: title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but only a username (not an email address) was provided by the upstream (proxy) server. Since Galaxy usernames are email addresses, a default mail domain must be set.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_maildomain</code> must be set before you may access Galaxy. """ return self.error( start_response, title, message ) user_accessible_paths = ( '/user/api_keys', '/user/edit_username', '/user/dbkeys', '/user/toolbox_filters', '/user/set_default_permissions', ) admin_accessible_paths = ( '/user/create', '/user/logout', '/user/manage_user_info', '/user/edit_info', '/userskeys/all_users', ) if not path_info.startswith('/user'): # shortcut the following whitelist for non-user-controller # requests. pass elif environ[self.remote_user_header] in self.admin_users and \ any([path_info.startswith(prefix) for prefix in admin_accessible_paths]): # If the user is an admin user, and any of the admin accessible paths match..., allow them to execute that action. pass elif any([path_info.startswith(prefix) for prefix in user_accessible_paths]): # If the user is allowed to access the path, pass pass elif path_info == '/user' or path_info == '/user/': pass # We do allow access to the root user preferences page. elif path_info.startswith( '/user' ): # Any other endpoint in the user controller is off limits title = "Access to Galaxy user controls is disabled" message = """ User controls are disabled when Galaxy is configured for external authentication. """ return self.error( start_response, title, message ) return self.app( environ, start_response ) else: log.debug("Unable to identify user. %s not found" % self.remote_user_header) for k, v in environ.iteritems(): log.debug("%s = %s" , k, v) title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but a username was not provided by the upstream (proxy) server. This is generally due to a misconfiguration in the upstream server.</p> <p>Please contact your local Galaxy administrator. """ return self.error( start_response, title, message )
def __call__(self, environ, start_response): environ['webapp'] = 'tool_shed' # Allow display servers if self.display_servers and 'REMOTE_ADDR' in environ: try: host = socket.gethostbyaddr(environ['REMOTE_ADDR'])[0] except (socket.error, socket.herror, socket.gaierror, socket.timeout): # in the event of a lookup failure, deny access host = None if host in self.display_servers: environ['HTTP_REMOTE_USER'] = '******' % ( self.maildomain or 'example.org') return self.app(environ, start_response) # If the secret header is enabled, we expect upstream to send along some key # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value # # This is not an ideal location for this function. The reason being # that because this check is done BEFORE the REMOTE_USER check, it is # possible to attack the GX_SECRET key without having correct # credentials. However, that's why it's not "ideal", but it is "good # enough". The only users able to exploit this are ones with access to # the local system (unless Galaxy is listening on 0.0.0.0....). It # seems improbable that an attacker with access to the server hosting # Galaxy would not have access to Galaxy itself, and be attempting to # attack the system if self.config_secret_header is not None: if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header): title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but an incorrect shared secret key was provided by the upstream (proxy) server.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_secret</code> and <code>GX_SECRET</code> header must be set before you may access Galaxy. """ return self.error(start_response, title, message) # Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is # un-authenticated. Any other possible values need to go here as well. path_info = environ.get('PATH_INFO', '') if 'HTTP_REMOTE_USER' in environ and environ[ 'HTTP_REMOTE_USER'] != '(null)': if not environ['HTTP_REMOTE_USER'].count('@'): if self.maildomain is not None: environ['HTTP_REMOTE_USER'] += '@' + self.maildomain else: title = "Access to this Galaxy tool shed is denied" message = """ This Galaxy tool shed is configured to authenticate users via an external method (such as HTTP authentication in Apache), but only a username (not an email address) was provided by the upstream (proxy) server. Since tool shed usernames are email addresses, a default mail domain must be set.</[> <p>The variable <code>remote_user_maildomain</code> must be set before you can access this tool shed. Contact your local tool shed administrator. """ return self.error(start_response, title, message) return self.app(environ, start_response) elif path_info.startswith('/api/'): # The API handles its own authentication via keys return self.app(environ, start_response) elif path_info.startswith('/user/api_keys'): # api_keys can be managed when remote_user is in use. pass else: title = "Access to this Galaxy tool shed is denied" message = """ This Galaxy tool shed is configured to authenticate users via an external method (such as HTTP authentication in Apache), but a username was not provided by the upstream (proxy) server. This is generally due to a misconfiguration in the upstream server.</p> <p>Contact your local Galaxy tool shed administrator. """ return self.error(start_response, title, message)
def __call__( self, environ, start_response ): environ[ 'webapp' ] = 'tool_shed' # Allow display servers if self.display_servers and environ.has_key( 'REMOTE_ADDR' ): try: host = socket.gethostbyaddr( environ[ 'REMOTE_ADDR' ] )[0] except( socket.error, socket.herror, socket.gaierror, socket.timeout ): # in the event of a lookup failure, deny access host = None if host in self.display_servers: environ[ 'HTTP_REMOTE_USER' ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' ) return self.app( environ, start_response ) # If the secret header is enabled, we expect upstream to send along some key # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value # # This is not an ideal location for this function. The reason being # that because this check is done BEFORE the REMOTE_USER check, it is # possible to attack the GX_SECRET key without having correct # credentials. However, that's why it's not "ideal", but it is "good # enough". The only users able to exploit this are ones with access to # the local system (unless Galaxy is listening on 0.0.0.0....). It # seems improbable that an attacker with access to the server hosting # Galaxy would not have access to Galaxy itself, and be attempting to # attack the system if self.config_secret_header is not None: if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header): title = "Access to Galaxy is denied" message = """ Galaxy is configured to authenticate users via an external method (such as HTTP authentication in Apache), but an incorrect shared secret key was provided by the upstream (proxy) server.</p> <p>Please contact your local Galaxy administrator. The variable <code>remote_user_secret</code> and <code>GX_SECRET</code> header must be set before you may access Galaxy. """ return self.error( start_response, title, message ) # Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is # un-authenticated. Any other possible values need to go here as well. path_info = environ.get('PATH_INFO', '') if environ.has_key( 'HTTP_REMOTE_USER' ) and environ[ 'HTTP_REMOTE_USER' ] != '(null)': if not environ[ 'HTTP_REMOTE_USER' ].count( '@' ): if self.maildomain is not None: environ[ 'HTTP_REMOTE_USER' ] += '@' + self.maildomain else: title = "Access to this Galaxy tool shed is denied" message = """ This Galaxy tool shed is configured to authenticate users via an external method (such as HTTP authentication in Apache), but only a username (not an email address) was provided by the upstream (proxy) server. Since tool shed usernames are email addresses, a default mail domain must be set.</[> <p>The variable <code>remote_user_maildomain</code> must be set before you can access this tool shed. Contact your local tool shed administrator. """ return self.error( start_response, title, message ) return self.app( environ, start_response ) elif path_info.startswith( '/api/' ): # The API handles its own authentication via keys return self.app( environ, start_response ) elif path_info.startswith( '/user/api_keys' ): # api_keys can be managed when remote_user is in use. pass else: title = "Access to this Galaxy tool shed is denied" message = """ This Galaxy tool shed is configured to authenticate users via an external method (such as HTTP authentication in Apache), but a username was not provided by the upstream (proxy) server. This is generally due to a misconfiguration in the upstream server.</p> <p>Contact your local Galaxy tool shed administrator. """ return self.error( start_response, title, message )