mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 12:13:33 +08:00
Replace api_inherit_admin with BootstrapAdminUser
This commit is contained in:
@@ -422,7 +422,7 @@ class CommonConfigurationMixin:
|
||||
|
||||
def is_admin_user(self, user):
|
||||
"""Determine if the provided user is listed in `admin_users`."""
|
||||
return user is not None and user.email in self.admin_users_list
|
||||
return user and (user.email in self.admin_users_list or user.bootstrap_admin_user)
|
||||
|
||||
@property
|
||||
def sentry_dsn_public(self):
|
||||
|
||||
@@ -103,13 +103,13 @@ class ProvidesUserContext:
|
||||
""" For transaction-like objects to provide Galaxy convience layer for
|
||||
reasoning about users.
|
||||
|
||||
Mixed in class must provide `user`, `api_inherit_admin`, and `app`
|
||||
Mixed in class must provide `user` and `app`
|
||||
properties.
|
||||
"""
|
||||
|
||||
@property
|
||||
def anonymous(self):
|
||||
return self.user is None and not self.api_inherit_admin
|
||||
return self.user is None
|
||||
|
||||
def get_current_user_roles(self):
|
||||
user = self.user
|
||||
@@ -121,7 +121,7 @@ class ProvidesUserContext:
|
||||
|
||||
@property
|
||||
def user_is_admin(self):
|
||||
return self.api_inherit_admin or self.app.config.is_admin_user(self.user)
|
||||
return self.app.config.is_admin_user(self.user)
|
||||
|
||||
@property
|
||||
def user_can_do_run_as(self):
|
||||
@@ -130,7 +130,7 @@ class ProvidesUserContext:
|
||||
return False
|
||||
user_in_run_as_users = self.user and self.user.email in run_as_users
|
||||
# Can do if explicitly in list or master_api_key supplied.
|
||||
can_do_run_as = user_in_run_as_users or self.api_inherit_admin
|
||||
can_do_run_as = user_in_run_as_users or self.user.bootstrap_admin_user
|
||||
return can_do_run_as
|
||||
|
||||
@property
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Manager and Serializer for Users.
|
||||
"""
|
||||
import hashlib
|
||||
import logging
|
||||
import random
|
||||
import socket
|
||||
@@ -14,7 +15,8 @@ from sqlalchemy.orm.exc import NoResultFound
|
||||
from galaxy import (
|
||||
exceptions,
|
||||
model,
|
||||
util
|
||||
schema,
|
||||
util,
|
||||
)
|
||||
from galaxy.managers import (
|
||||
api_keys,
|
||||
@@ -239,6 +241,8 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
try:
|
||||
provided_key = sa_session.query(self.app.model.APIKeys).filter(self.app.model.APIKeys.table.c.key == api_key).one()
|
||||
except NoResultFound:
|
||||
if self.check_master_api_key(api_key=api_key):
|
||||
return schema.BoostrapAdminUser()
|
||||
raise exceptions.AuthenticationFailed('Provided API key is not valid.')
|
||||
if provided_key.user.deleted:
|
||||
raise exceptions.AuthenticationFailed('User account is deactivated, please contact an administrator.')
|
||||
@@ -247,6 +251,15 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
raise exceptions.AuthenticationFailed('Provided API key has expired.')
|
||||
return provided_key.user
|
||||
|
||||
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(util.smart_str(master_api_key)).hexdigest()
|
||||
provided_hash = hashlib.sha256(util.smart_str(api_key)).hexdigest()
|
||||
return util.safe_str_cmp(master_hash, provided_hash)
|
||||
|
||||
# ---- admin
|
||||
def is_admin(self, user, trans=None):
|
||||
"""Return True if this user is an admin (or session is authenticated as admin).
|
||||
|
||||
@@ -356,6 +356,7 @@ class JobLike:
|
||||
|
||||
class User(Dictifiable, RepresentById):
|
||||
use_pbkdf2 = True
|
||||
bootstrap_admin_user = False
|
||||
"""
|
||||
Data for a Galaxy user or admin and relations to their
|
||||
histories, credentials, and roles.
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import typing
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BoostrapAdminUser(BaseModel):
|
||||
id = 0
|
||||
email: typing.Optional[str] = None
|
||||
bootstrap_admin_user = True
|
||||
|
||||
def all_roles(*args) -> list:
|
||||
return []
|
||||
|
||||
@@ -161,7 +161,6 @@ def legacy_expose_api(func, to_json=True, user_required=True):
|
||||
return "Malformed user id ( %s ) specified, unable to decode." % str(kwargs['payload']['run_as'])
|
||||
try:
|
||||
user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id)
|
||||
trans.api_inherit_admin = trans.user_is_admin
|
||||
trans.set_user(user)
|
||||
except Exception:
|
||||
trans.response.status = 400
|
||||
@@ -286,7 +285,6 @@ def expose_api(func, to_json=True, user_required=True, user_or_session_required=
|
||||
return __api_error_response(trans, err_code=error_code, err_msg=error_message, status_code=400)
|
||||
try:
|
||||
user = trans.sa_session.query(trans.app.model.User).get(decoded_user_id)
|
||||
trans.api_inherit_admin = trans.user_is_admin
|
||||
trans.set_user(user)
|
||||
except Exception:
|
||||
error_code = error_codes.USER_INVALID_RUN_AS
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""
|
||||
"""
|
||||
import datetime
|
||||
import hashlib
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
@@ -33,8 +32,6 @@ from galaxy.managers.users import UserManager
|
||||
from galaxy.util import (
|
||||
asbool,
|
||||
safe_makedirs,
|
||||
safe_str_cmp,
|
||||
smart_str,
|
||||
unicodify
|
||||
)
|
||||
from galaxy.util.sanitize_html import sanitize_html
|
||||
@@ -204,8 +201,6 @@ class GalaxyWebTransaction(base.DefaultWebTransaction,
|
||||
# that the current history should not be used for parameter values
|
||||
# and such).
|
||||
self.workflow_building_mode = False
|
||||
# Flag indicating whether this is an API call and the API key user is an administrator
|
||||
self.api_inherit_admin = False
|
||||
self.__user = None
|
||||
self.galaxy_session = None
|
||||
self.error_message = None
|
||||
@@ -362,9 +357,10 @@ class GalaxyWebTransaction(base.DefaultWebTransaction,
|
||||
def set_user(self, user):
|
||||
"""Set the current user."""
|
||||
if self.galaxy_session:
|
||||
self.galaxy_session.user = user
|
||||
self.sa_session.add(self.galaxy_session)
|
||||
self.sa_session.flush()
|
||||
if user.bootstrap_admin_user:
|
||||
self.galaxy_session.user = user
|
||||
self.sa_session.add(self.galaxy_session)
|
||||
self.sa_session.flush()
|
||||
self.__user = user
|
||||
|
||||
user = property(get_user, set_user)
|
||||
@@ -407,12 +403,7 @@ class GalaxyWebTransaction(base.DefaultWebTransaction,
|
||||
api_key = self.request.params.get('key', None) or self.request.headers.get('x-api-key', None)
|
||||
secure_id = self.get_cookie(name=session_cookie)
|
||||
api_key_supplied = self.environ.get('is_api_request', False) and api_key
|
||||
if api_key_supplied and self._check_master_api_key(api_key):
|
||||
self.api_inherit_admin = True
|
||||
log.info("Session authenticated using Galaxy master api key")
|
||||
self.user = None
|
||||
self.galaxy_session = None
|
||||
elif api_key_supplied:
|
||||
if api_key_supplied:
|
||||
# Sessionless API transaction, we just need to associate a user.
|
||||
try:
|
||||
user = self.user_manager.by_api_key(api_key)
|
||||
@@ -434,15 +425,6 @@ class GalaxyWebTransaction(base.DefaultWebTransaction,
|
||||
self.user = None
|
||||
self.galaxy_session = None
|
||||
|
||||
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(smart_str(master_api_key)).hexdigest()
|
||||
provided_hash = hashlib.sha256(smart_str(api_key)).hexdigest()
|
||||
return safe_str_cmp(master_hash, provided_hash)
|
||||
|
||||
def _ensure_valid_session(self, session_cookie, create=True):
|
||||
"""
|
||||
Ensure that a valid Galaxy session exists and is available as
|
||||
|
||||
@@ -222,7 +222,7 @@ class UserAPIController(BaseAPIController, UsesTagsMixin, CreatesApiKeysMixin, B
|
||||
|
||||
# only allow updating other users if they're admin
|
||||
editing_someone_else = current_user != user_to_update
|
||||
is_admin = trans.api_inherit_admin or self.user_manager.is_admin(current_user)
|
||||
is_admin = self.user_manager.is_admin(current_user)
|
||||
if editing_someone_else and not is_admin:
|
||||
raise exceptions.InsufficientPermissionsException('You are not allowed to update that user', id=id)
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ class WorkRequestContext(ProvidesAppContext, ProvidesUserContext, ProvidesHistor
|
||||
self.__user = user
|
||||
self.__user_current_roles = None
|
||||
self.__history = history
|
||||
self.api_inherit_admin = False
|
||||
self.workflow_building_mode = workflow_building_mode
|
||||
|
||||
def get_history(self, create=False):
|
||||
|
||||
@@ -33,6 +33,7 @@ class APIKeys:
|
||||
class User(Dictifiable):
|
||||
dict_collection_visible_keys = ['id', 'username']
|
||||
dict_element_visible_keys = ['id', 'username']
|
||||
bootstrap_admin_user = False
|
||||
|
||||
def __init__(self, email=None, password=None):
|
||||
self.email = email
|
||||
|
||||
@@ -211,6 +211,7 @@ class MockTrans:
|
||||
self.error_message = None
|
||||
self.anonymous = False
|
||||
self.debug = True
|
||||
self.user_is_admin = False
|
||||
|
||||
self.galaxy_session = None
|
||||
self.__user = user
|
||||
|
||||
Reference in New Issue
Block a user