def replace_sample_acls(self, id_: UUID, user: UserID, new_acls: SampleACLOwnerless, as_admin: bool = False) -> None: ''' Completely replace a sample's ACLs. The owner cannot be changed. :param id_: the sample's ID. :param user: the user changing the ACLs. :param new_acls: the new ACLs. :param as_admin: Skip ACL checks. :raises NoSuchUserError: if any of the users in the ACLs do not exist. :raises NoSuchSampleError: if the sample does not exist. :raises UnauthorizedError: if the user does not have admin permission for the sample or the request attempts to change the owner. :raises SampleStorageError: if the sample could not be retrieved. ''' _not_falsy(id_, 'id_') _not_falsy(user, 'user') _not_falsy(new_acls, 'new_acls') try: bad_users = self._user_lookup.invalid_users( _cast(List[UserID], []) + list(new_acls.admin) + list(new_acls.write) + list(new_acls.read)) # let authentication errors propagate, not much to do # could add retries to the client except _user_lookup_mod.InvalidUserError as e: raise _NoSuchUserError(e.args[0]) from e except _user_lookup_mod.InvalidTokenError: raise ValueError( 'user lookup token for KBase auth server is invalid, cannot continue' ) if bad_users: raise _NoSuchUserError(', '.join([u.id for u in bad_users[:5]])) count = 0 while count >= 0: if count >= 5: raise ValueError( f'Failed setting ACLs after 5 attempts for sample {id_}') acls = self._storage.get_sample_acls(id_) self._check_perms(id_, user, _SampleAccessType.ADMIN, acls, as_admin=as_admin) new_acls = SampleACL(acls.owner, new_acls.admin, new_acls.write, new_acls.read) try: self._storage.replace_sample_acls(id_, new_acls) count = -1 except _OwnerChangedError: count += 1
def _check_for_bad_users(self, users: List[UserID]): try: bad_users = self._user_lookup.invalid_users(users) # let authentication errors propagate, not much to do # could add retries to the client except _user_lookup_mod.InvalidUserError as e: raise _NoSuchUserError(e.args[0]) from e except _user_lookup_mod.InvalidTokenError: raise ValueError('user lookup token for KBase auth server is invalid, cannot continue') if bad_users: raise _NoSuchUserError(', '.join([u.id for u in bad_users[:5]]))
def get_user_workspaces(self, user: UserID) -> List[int]: ''' Get a list of IDs of workspaces a user can read, including public workspaces. :param user: The username of the user whose workspaces will be returned. :returns: A list of workspace IDs. :raises NoSuchUserError: if the user does not exist. ''' # May also want write / admin / no public ws try: ids = self._ws.administer({ 'command': 'listWorkspaceIDs', 'user': _not_falsy(user, 'user').id, 'params': { 'perm': 'r', 'excludeGlobal': 0 } }) except _ServerError as se: # this is pretty ugly, need error codes if 'not a valid user' in se.args[0]: raise _NoSuchUserError(se.args[0]) from se else: raise return sorted(ids['workspaces'] + ids['pub'])
def check_admin(user_lookup: KBaseUserLookup, token: Optional[str], perm: AdminPermission, method: str, log_fn: Callable[[str], None], as_user: UserID = None, skip_check: bool = False) -> bool: ''' Check whether a user has admin privileges. The request is logged. :param user_lookup: the service to use to look up user information. :param token: the user's token, or None if the user is anonymous. In this case, if skip_check is false, an UnauthorizedError will be thrown. :param perm: the required administration permission. :param method: the method the user is trying to run. This is used in logging and error messages. :param logger: a function that logs information when called with a string. :param as_user: if the admin is impersonating another user, the username of that user. :param skip_check: Skip the administration permission check and return false. :returns: true if the user has the required administration permission, false if skip_check is true. :raises UnauthorizedError: if the user does not have the permission required. :raises InvalidUserError: if any of the user names are invalid. :raises UnauthorizedError: if any of the users names are valid but do not exist in the system. ''' if skip_check: return False if not token: raise _UnauthorizedError( 'Anonymous users may not act as service administrators.') _not_falsy(method, 'method') _not_falsy(log_fn, 'log_fn') if _not_falsy(perm, 'perm') == AdminPermission.NONE: raise ValueError( 'what are you doing calling this method with no permission ' + 'requirement? That totally makes no sense. Get a brain moran') if as_user and perm != AdminPermission.FULL: raise ValueError('as_user is supplied, but permission is not FULL') p, user = _not_falsy(user_lookup, 'user_lookup').is_admin(token) if p < perm: err = (f'User {user} does not have the necessary administration ' + f'privileges to run method {method}') log_fn(err) raise _UnauthorizedError(err) if as_user and user_lookup.invalid_users([as_user ]): # returns list of bad users raise _NoSuchUserError(as_user.id) log_fn( f'User {user} is running method {method} with administration permission {p.name}' + (f' as user {as_user}' if as_user else '')) return True