Delegated superuser API access

Add a new scope for SUPERUSER that allows delegated access to the superuser endpoints. CA needs this so they can programmatically create and remove users.
This commit is contained in:
Joseph Schorr 2015-06-28 11:22:34 +03:00
parent d9ce8fdf52
commit 87efcb9e3d
4 changed files with 41 additions and 4 deletions

View file

@ -27,7 +27,7 @@ _SuperUserNeed = partial(namedtuple('superuserneed', ['type']), 'superuser')
REPO_ROLES = [None, 'read', 'write', 'admin']
TEAM_ROLES = [None, 'member', 'creator', 'admin']
USER_ROLES = [None, 'read', 'admin']
USER_ROLES = [None, 'read', 'admin', 'superuser']
TEAM_REPO_ROLES = {
'admin': 'admin',
@ -54,6 +54,7 @@ SCOPE_MAX_USER_ROLES = defaultdict(lambda: None)
SCOPE_MAX_USER_ROLES.update({
scopes.READ_USER: 'read',
scopes.DIRECT_LOGIN: 'admin',
scopes.SUPERUSER: 'superuser',
})
@ -113,8 +114,14 @@ class QuayDeferredPermissionUser(Identity):
if user_object is None:
return super(QuayDeferredPermissionUser, self).can(permission)
# Add the superuser need, if applicable.
if superusers.is_superuser(user_object.username):
# Add the superuser need, if applicable:
# - If the user's role is an admin (direct login) and they are a superuser
# - If the user has granted the superuser scope
superuser_role = self._user_role_for_scopes('superuser')
if superuser_role == 'admin' and superusers.is_superuser(user_object.username):
self.provides.add(_SuperUserNeed())
elif superuser_role == 'superuser':
self.provides.add(_SuperUserNeed())
# Add the user specific permissions, only for non-oauth permission

View file

@ -1,5 +1,5 @@
from collections import namedtuple
import features
Scope = namedtuple('scope', ['scope', 'icon', 'dangerous', 'title', 'description'])
@ -59,6 +59,15 @@ DIRECT_LOGIN = Scope(scope='direct_user_login',
description=('This scope should not be available to OAuth applications. '
'Never approve a request for this scope!'))
SUPERUSER = Scope(scope='super:user',
icon='fa-street-view',
dangerous=True,
title='Super User Access',
description=('This application will be able to administer your installation '
'including managing users, managing organizations and other '
'features found in the superuser panel. You should have '
'absolute trust in the requesting application before granting this '
'permission.'))
ALL_SCOPES = {scope.scope:scope for scope in (READ_REPO, WRITE_REPO, ADMIN_REPO, CREATE_REPO,
READ_USER, ORG_ADMIN)}
@ -73,6 +82,9 @@ IMPLIED_SCOPES = {
None: set(),
}
if features.SUPER_USERS:
ALL_SCOPES[SUPERUSER.scope] = SUPERUSER
IMPLIED_SCOPES[SUPERUSER] = {SUPERUSER}
def scopes_from_scope_string(scopes):
if not scopes:

View file

@ -308,6 +308,10 @@ def require_fresh_login(func):
if not user:
raise Unauthorized()
oauth_token = get_validated_oauth_token()
if oauth_token:
return func(*args, **kwargs)
logger.debug('Checking fresh login for user %s', user.username)
last_login = session.get('login_time', datetime.datetime.min)

View file

@ -19,6 +19,7 @@ from endpoints.api.logs import get_logs
from data import model
from auth.permissions import SuperUserPermission
from auth.auth_context import get_authenticated_user
from auth import scopes
from util.useremails import send_confirmation_email, send_recovery_email
import features
@ -42,6 +43,7 @@ class SuperUserGetLogsForService(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('getSystemLogs')
@require_scope(scopes.SUPERUSER)
def get(self, service):
""" Returns the logs for the specific service. """
if SuperUserPermission().can():
@ -70,6 +72,7 @@ class SuperUserSystemLogServices(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('listSystemLogServices')
@require_scope(scopes.SUPERUSER)
def get(self):
""" List the system logs for the current system. """
if SuperUserPermission().can():
@ -93,6 +96,7 @@ class SuperUserLogs(ApiResource):
@query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('performer', 'Username for which to filter logs.', type=str)
@require_scope(scopes.SUPERUSER)
def get(self, args):
""" List the usage logs for the current system. """
if SuperUserPermission().can():
@ -129,6 +133,7 @@ class ChangeLog(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('getChangeLog')
@require_scope(scopes.SUPERUSER)
def get(self):
""" Returns the change log for this installation. """
if SuperUserPermission().can():
@ -149,6 +154,7 @@ class SuperUserOrganizationList(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('listAllOrganizations')
@require_scope(scopes.SUPERUSER)
def get(self):
""" Returns a list of all organizations in the system. """
if SuperUserPermission().can():
@ -187,6 +193,7 @@ class SuperUserList(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('listAllUsers')
@require_scope(scopes.SUPERUSER)
def get(self):
""" Returns a list of all users in the system. """
if SuperUserPermission().can():
@ -202,6 +209,7 @@ class SuperUserList(ApiResource):
@verify_not_prod
@nickname('createInstallUser')
@validate_json_request('CreateInstallUser')
@require_scope(scopes.SUPERUSER)
def post(self):
""" Creates a new user. """
user_information = request.get_json()
@ -239,6 +247,7 @@ class SuperUserSendRecoveryEmail(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('sendInstallUserRecoveryEmail')
@require_scope(scopes.SUPERUSER)
def post(self, username):
if SuperUserPermission().can():
user = model.get_nonrobot_user(username)
@ -288,6 +297,7 @@ class SuperUserManagement(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('getInstallUser')
@require_scope(scopes.SUPERUSER)
def get(self, username):
""" Returns information about the specified user. """
if SuperUserPermission().can():
@ -302,6 +312,7 @@ class SuperUserManagement(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('deleteInstallUser')
@require_scope(scopes.SUPERUSER)
def delete(self, username):
""" Deletes the specified user. """
if SuperUserPermission().can():
@ -321,6 +332,7 @@ class SuperUserManagement(ApiResource):
@verify_not_prod
@nickname('changeInstallUser')
@validate_json_request('UpdateUser')
@require_scope(scopes.SUPERUSER)
def put(self, username):
""" Updates information about the specified user. """
if SuperUserPermission().can():
@ -371,6 +383,7 @@ class SuperUserOrganizationManagement(ApiResource):
@require_fresh_login
@verify_not_prod
@nickname('deleteOrganization')
@require_scope(scopes.SUPERUSER)
def delete(self, name):
""" Deletes the specified organization. """
if SuperUserPermission().can():
@ -385,6 +398,7 @@ class SuperUserOrganizationManagement(ApiResource):
@verify_not_prod
@nickname('changeOrganization')
@validate_json_request('UpdateOrg')
@require_scope(scopes.SUPERUSER)
def put(self, name):
""" Updates information about the specified user. """
if SuperUserPermission().can():