Add a user info scope and thread it through the code. Protect the org modification API.

This commit is contained in:
jakedt 2014-03-18 19:21:27 -04:00
parent 89556172d5
commit 64071b9e8e
13 changed files with 144 additions and 115 deletions

View file

@ -53,7 +53,8 @@ def process_basic_auth(auth):
logger.debug('Successfully validated robot: %s' % credentials[0]) logger.debug('Successfully validated robot: %s' % credentials[0])
set_authenticated_user(robot) set_authenticated_user(robot)
deferred_robot = QuayDeferredPermissionUser(robot.username, 'username') deferred_robot = QuayDeferredPermissionUser(robot.username, 'username',
{'direct_user_login'})
identity_changed.send(app, identity=deferred_robot) identity_changed.send(app, identity=deferred_robot)
return return
except model.InvalidRobotException: except model.InvalidRobotException:
@ -66,7 +67,8 @@ def process_basic_auth(auth):
logger.debug('Successfully validated user: %s' % authenticated.username) logger.debug('Successfully validated user: %s' % authenticated.username)
set_authenticated_user(authenticated) set_authenticated_user(authenticated)
new_identity = QuayDeferredPermissionUser(authenticated.username, 'username') new_identity = QuayDeferredPermissionUser(authenticated.username, 'username',
{'direct_user_login'})
identity_changed.send(app, identity=new_identity) identity_changed.send(app, identity=new_identity)
return return
@ -150,7 +152,7 @@ def process_oauth(f):
elif not current_user.is_anonymous(): elif not current_user.is_anonymous():
logger.debug('Loading user from cookie: %s', current_user.get_id()) logger.debug('Loading user from cookie: %s', current_user.get_id())
set_authenticated_user_deferred(current_user.get_id()) set_authenticated_user_deferred(current_user.get_id())
loaded = QuayDeferredPermissionUser(current_user.get_id(), 'username') loaded = QuayDeferredPermissionUser(current_user.get_id(), 'username', {'direct_user_login'})
identity_changed.send(app, identity=loaded) identity_changed.send(app, identity=loaded)
else: else:
logger.debug('No auth header or login cookie.') logger.debug('No auth header or login cookie.')

View file

@ -1,9 +1,7 @@
import logging import logging
from flask.ext.principal import (identity_loaded, UserNeed, Permission, from flask.ext.principal import identity_loaded, Permission, Identity, identity_changed
Identity, identity_changed) from collections import namedtuple, defaultdict
from flask.ext.login import current_user
from collections import namedtuple
from functools import partial from functools import partial
from data import model from data import model
@ -17,27 +15,36 @@ _ResourceNeed = namedtuple('resource', ['type', 'namespace', 'name', 'role'])
_RepositoryNeed = partial(_ResourceNeed, 'repository') _RepositoryNeed = partial(_ResourceNeed, 'repository')
_OrganizationNeed = namedtuple('organization', ['orgname', 'role']) _OrganizationNeed = namedtuple('organization', ['orgname', 'role'])
_TeamNeed = namedtuple('orgteam', ['orgname', 'teamname', 'role']) _TeamNeed = namedtuple('orgteam', ['orgname', 'teamname', 'role'])
_UserNeed = namedtuple('user', ['username', 'role'])
REPO_ROLES = [None, 'read', 'write', 'admin'] REPO_ROLES = [None, 'read', 'write', 'admin']
TEAM_ROLES = [None, 'member', 'creator', 'admin'] TEAM_ROLES = [None, 'member', 'creator', 'admin']
USER_ROLES = [None, 'read', 'admin']
SCOPE_MAX_REPO_ROLES = { SCOPE_MAX_REPO_ROLES = defaultdict(lambda: None)
SCOPE_MAX_REPO_ROLES.update({
'repo:read': 'read', 'repo:read': 'read',
'repo:write': 'write', 'repo:write': 'write',
'repo:admin': 'admin', 'repo:admin': 'admin',
'repo:create': None, 'direct_user_login': 'admin',
} })
SCOPE_MAX_TEAM_ROLES = { SCOPE_MAX_TEAM_ROLES = defaultdict(lambda: None)
'repo:read': None, SCOPE_MAX_TEAM_ROLES.update({
'repo:write': None,
'repo:admin': None,
'repo:create': 'creator', 'repo:create': 'creator',
} 'direct_user_login': 'admin',
})
SCOPE_MAX_USER_ROLES = defaultdict(lambda: None)
SCOPE_MAX_USER_ROLES.update({
'user:read': 'admin',
'direct_user_login': 'admin',
})
class QuayDeferredPermissionUser(Identity): class QuayDeferredPermissionUser(Identity):
def __init__(self, id, auth_type=None, scopes=None): def __init__(self, id, auth_type, scopes):
super(QuayDeferredPermissionUser, self).__init__(id, auth_type) super(QuayDeferredPermissionUser, self).__init__(id, auth_type)
self._permissions_loaded = False self._permissions_loaded = False
@ -61,16 +68,18 @@ class QuayDeferredPermissionUser(Identity):
def _repo_role_for_scopes(self, role): def _repo_role_for_scopes(self, role):
return self._translate_role_for_scopes(REPO_ROLES, SCOPE_MAX_REPO_ROLES, role) return self._translate_role_for_scopes(REPO_ROLES, SCOPE_MAX_REPO_ROLES, role)
def _user_role_for_scopes(self, role):
return self._translate_role_for_scopes(USER_ROLES, SCOPE_MAX_USER_ROLES, role)
def can(self, permission): def can(self, permission):
if not self._permissions_loaded: if not self._permissions_loaded:
logger.debug('Loading user permissions after deferring.') logger.debug('Loading user permissions after deferring.')
user_object = model.get_user(self.id) user_object = model.get_user(self.id)
# Add the user specific permissions, only for non-oauth permission # Add the user specific permissions, only for non-oauth permission
if self._scope_set is None: user_grant = _UserNeed(user_object.username, self._user_role_for_scopes('admin'))
user_grant = UserNeed(user_object.username)
self.provides.add(user_grant) self.provides.add(user_grant)
logger.debug('Add admin to user namespace: %s', user_object.username) logger.debug('User permission: {0}'.format(user_grant))
# Every user is the admin of their own 'org' # Every user is the admin of their own 'org'
user_namespace = _OrganizationNeed(user_object.username, self._team_role_for_scopes('admin')) user_namespace = _OrganizationNeed(user_object.username, self._team_role_for_scopes('admin'))
@ -135,10 +144,17 @@ class CreateRepositoryPermission(Permission):
create_repo_org) create_repo_org)
class UserPermission(Permission): class UserAdminPermission(Permission):
def __init__(self, username): def __init__(self, username):
user_need = UserNeed(username) user_admin = _UserNeed(username, 'admin')
super(UserPermission, self).__init__(user_need) super(UserAdminPermission, self).__init__(user_admin)
class UserReadPermission(Permission):
def __init__(self, username):
user_admin = _UserNeed(username, 'admin')
user_read = _UserNeed(username, 'read')
super(UserReadPermission, self).__init__(user_read, user_admin)
class AdministerOrganizationPermission(Permission): class AdministerOrganizationPermission(Permission):
@ -176,7 +192,7 @@ def on_identity_loaded(sender, identity):
elif identity.auth_type == 'username': elif identity.auth_type == 'username':
logger.debug('Switching username permission to deferred object: %s', identity.id) logger.debug('Switching username permission to deferred object: %s', identity.id)
switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'username') switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'username', {'direct_user_login'})
identity_changed.send(app, identity=switch_to_deferred) identity_changed.send(app, identity=switch_to_deferred)
elif identity.auth_type == 'token': elif identity.auth_type == 'token':

View file

@ -10,16 +10,16 @@ WRITE_REPO = {
'scope': 'repo:write', 'scope': 'repo:write',
'icon': 'fa-hdd-o', 'icon': 'fa-hdd-o',
'title': 'Read/Write to any accessible repositories', 'title': 'Read/Write to any accessible repositories',
'description': ('This application will be able to view, push and pull to all repositories to which the ' 'description': ('This application will be able to view, push and pull to all repositories to '
'granting user or robot account has write access') 'which the granting user or robot account has write access')
} }
ADMIN_REPO = { ADMIN_REPO = {
'scope': 'repo:admin', 'scope': 'repo:admin',
'icon': 'fa-hdd-o', 'icon': 'fa-hdd-o',
'title': 'Administer Repositories', 'title': 'Administer Repositories',
'description': ('This application will have administrator access to all repositories to which the ' 'description': ('This application will have administrator access to all repositories to which '
'granting user or robot account has access') 'the granting user or robot account has access')
} }
CREATE_REPO = { CREATE_REPO = {
@ -30,7 +30,16 @@ CREATE_REPO = {
'the granting user or robot account is allowed to create repositories') 'the granting user or robot account is allowed to create repositories')
} }
ALL_SCOPES = {scope['scope']:scope for scope in (READ_REPO, WRITE_REPO, ADMIN_REPO, CREATE_REPO)} USER_READ = {
'scope': 'user:read',
'icon': 'fa-user',
'title': 'Read User Information',
'description': ('This application will be able to read user information such as username and '
'email address.'),
}
ALL_SCOPES = {scope['scope']:scope for scope in (READ_REPO, WRITE_REPO, ADMIN_REPO, CREATE_REPO,
USER_READ)}
def scopes_from_scope_string(scopes): def scopes_from_scope_string(scopes):

View file

@ -12,9 +12,9 @@ from jsonschema import validate, ValidationError
from data import model from data import model
from util.names import parse_namespace_repository from util.names import parse_namespace_repository
from auth.permissions import (ReadRepositoryPermission, from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission,
ModifyRepositoryPermission, AdministerRepositoryPermission, UserReadPermission,
AdministerRepositoryPermission) UserAdminPermission)
from auth import scopes from auth import scopes
from auth.auth_context import get_authenticated_user, get_validated_oauth_token from auth.auth_context import get_authenticated_user, get_validated_oauth_token
from auth.auth import process_oauth from auth.auth import process_oauth
@ -183,6 +183,29 @@ require_repo_write = require_repo_permission(ModifyRepositoryPermission, scopes.
require_repo_admin = require_repo_permission(AdministerRepositoryPermission, scopes.ADMIN_REPO) require_repo_admin = require_repo_permission(AdministerRepositoryPermission, scopes.ADMIN_REPO)
def require_user_permission(permission_class, scope=None):
def wrapper(func):
@add_method_metadata('oauth2_scope', scope)
@wraps(func)
def wrapped(self, *args, **kwargs):
user = get_authenticated_user()
if not user:
logger.debug('User is anonymous.')
raise InvalidToken('Method requires an auth token or user login.')
logger.debug('Checking permission %s for user', permission_class, user.username)
permission = permission_class(user.username)
if permission.can():
return func(self, *args, **kwargs)
raise Unauthorized()
return wrapped
return wrapper
require_user_read = require_user_permission(UserReadPermission, scopes.USER_READ)
require_user_admin = require_user_permission(UserAdminPermission, None)
def require_scope(scope_object): def require_scope(scope_object):
def wrapper(func): def wrapper(func):
@add_method_metadata('oauth2_scope', scope_object['scope']) @add_method_metadata('oauth2_scope', scope_object['scope'])

View file

@ -3,7 +3,8 @@ import stripe
from flask import request from flask import request
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action, from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action,
related_user_resource, internal_only, Unauthorized, NotFound) related_user_resource, internal_only, Unauthorized, NotFound,
require_user_admin)
from endpoints.api.subscribe import subscribe, subscription_view from endpoints.api.subscribe import subscribe, subscription_view
from auth.permissions import AdministerOrganizationPermission from auth.permissions import AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
@ -109,23 +110,19 @@ class UserCard(ApiResource):
}, },
} }
@require_user_admin
@nickname('getUserCard') @nickname('getUserCard')
def get(self): def get(self):
""" Get the user's credit card. """ """ Get the user's credit card. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
return get_card(user) return get_card(user)
@require_user_admin
@nickname('setUserCard') @nickname('setUserCard')
@validate_json_request('UserCard') @validate_json_request('UserCard')
def post(self): def post(self):
""" Update the user's credit card. """ """ Update the user's credit card. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
token = request.get_json()['token'] token = request.get_json()['token']
response = set_card(user, token) response = set_card(user, token)
log_action('account_change_cc', user.username) log_action('account_change_cc', user.username)
@ -204,6 +201,7 @@ class UserPlan(ApiResource):
}, },
} }
@require_user_admin
@nickname('updateUserSubscription') @nickname('updateUserSubscription')
@validate_json_request('UserSubscription') @validate_json_request('UserSubscription')
def put(self): def put(self):
@ -212,18 +210,13 @@ class UserPlan(ApiResource):
plan = request_data['plan'] plan = request_data['plan']
token = request_data['token'] if 'token' in request_data else None token = request_data['token'] if 'token' in request_data else None
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
return subscribe(user, plan, token, False) # Business features not required return subscribe(user, plan, token, False) # Business features not required
@require_user_admin
@nickname('getUserSubscription') @nickname('getUserSubscription')
def get(self): def get(self):
""" Fetch any existing subscription for the user. """ """ Fetch any existing subscription for the user. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
private_repos = model.get_private_repo_count(user.username) private_repos = model.get_private_repo_count(user.username)
if user.stripe_id: if user.stripe_id:
@ -302,13 +295,11 @@ class OrganizationPlan(ApiResource):
@resource('/v1/user/invoices') @resource('/v1/user/invoices')
class UserInvoiceList(ApiResource): class UserInvoiceList(ApiResource):
""" Resource for listing a user's invoices. """ """ Resource for listing a user's invoices. """
@require_user_admin
@nickname('listUserInvoices') @nickname('listUserInvoices')
def get(self): def get(self):
""" List the invoices for the current user. """ """ List the invoices for the current user. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
if not user.stripe_id: if not user.stripe_id:
raise NotFound() raise NotFound()

View file

@ -4,7 +4,7 @@ from datetime import datetime, timedelta
from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args, from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args,
RepositoryParamResource, require_repo_admin, related_user_resource, RepositoryParamResource, require_repo_admin, related_user_resource,
format_date, Unauthorized, NotFound) format_date, Unauthorized, NotFound, require_user_admin)
from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from data import model from data import model
@ -84,6 +84,7 @@ class RepositoryLogs(RepositoryParamResource):
@resource('/v1/user/logs') @resource('/v1/user/logs')
class UserLogs(ApiResource): class UserLogs(ApiResource):
""" Resource for fetching logs for the current user. """ """ Resource for fetching logs for the current user. """
@require_user_admin
@nickname('listUserLogs') @nickname('listUserLogs')
@parse_args @parse_args
@query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str) @query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str)
@ -96,9 +97,6 @@ class UserLogs(ApiResource):
end_time = args['endtime'] end_time = args['endtime']
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
return get_logs(user.username, start_time, end_time, performer_name=performer_name) return get_logs(user.username, start_time, end_time, performer_name=performer_name)

View file

@ -4,7 +4,8 @@ import stripe
from flask import request from flask import request
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error, from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
related_user_resource, internal_only, Unauthorized, NotFound) related_user_resource, internal_only, Unauthorized, NotFound,
require_user_admin)
from endpoints.api.team import team_view from endpoints.api.team import team_view
from endpoints.api.user import User, PrivateRepositories from endpoints.api.user import User, PrivateRepositories
from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission, from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission,
@ -62,14 +63,12 @@ class OrganizationList(ApiResource):
}, },
} }
@require_user_admin
@nickname('createOrganization') @nickname('createOrganization')
@validate_json_request('NewOrg') @validate_json_request('NewOrg')
def post(self): def post(self):
""" Create a new organization. """ """ Create a new organization. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
org_data = request.get_json() org_data = request.get_json()
existing = None existing = None
@ -135,6 +134,8 @@ class Organization(ApiResource):
@validate_json_request('UpdateOrg') @validate_json_request('UpdateOrg')
def put(self, orgname): def put(self, orgname):
""" Change the details for the specified organization. """ """ Change the details for the specified organization. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
@ -155,6 +156,7 @@ class Organization(ApiResource):
teams = model.get_teams_within_org(org) teams = model.get_teams_within_org(org)
return org_view(org, teams) return org_view(org, teams)
raise Unauthorized()
@resource('/v1/organization/<orgname>/private') @resource('/v1/organization/<orgname>/private')

View file

@ -1,16 +1,15 @@
import logging import logging
import json import json
from flask import current_app, request from flask import request
from data import model from data import model
from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request, from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request,
require_repo_read, require_repo_write, require_repo_admin, require_repo_read, require_repo_write, require_repo_admin,
RepositoryParamResource, resource, query_param, parse_args, ApiResource, RepositoryParamResource, resource, query_param, parse_args, ApiResource,
request_error, require_scope, Unauthorized, NotFound) request_error, require_scope, Unauthorized, NotFound, InvalidRequest)
from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission, from auth.permissions import (ModifyRepositoryPermission, AdministerRepositoryPermission,
AdministerRepositoryPermission, CreateRepositoryPermission) CreateRepositoryPermission)
from auth.auth import process_auth
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from auth import scopes from auth import scopes
@ -63,10 +62,11 @@ class RepositoryList(ApiResource):
def post(self): def post(self):
"""Create a new repository.""" """Create a new repository."""
owner = get_authenticated_user() owner = get_authenticated_user()
if not owner:
raise Unauthorized()
req = request.get_json() req = request.get_json()
if owner is None and 'namespace' not in 'req':
raise InvalidRequest('Must provide a namespace or must be logged in.')
namespace_name = req['namespace'] if 'namespace' in req else owner.username namespace_name = req['namespace'] if 'namespace' in req else owner.username
permission = CreateRepositoryPermission(namespace_name) permission = CreateRepositoryPermission(namespace_name)

View file

@ -1,5 +1,5 @@
from endpoints.api import (resource, nickname, ApiResource, log_action, related_user_resource, from endpoints.api import (resource, nickname, ApiResource, log_action, related_user_resource,
Unauthorized) Unauthorized, require_user_admin)
from auth.permissions import AdministerOrganizationPermission, OrganizationMemberPermission from auth.permissions import AdministerOrganizationPermission, OrganizationMemberPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from data import model from data import model
@ -16,13 +16,11 @@ def robot_view(name, token):
@resource('/v1/user/robots') @resource('/v1/user/robots')
class UserRobotList(ApiResource): class UserRobotList(ApiResource):
""" Resource for listing user robots. """ """ Resource for listing user robots. """
@require_user_admin
@nickname('getUserRobots') @nickname('getUserRobots')
def get(self): def get(self):
""" List the available robots for the user. """ """ List the available robots for the user. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
robots = model.list_entity_robots(user.username) robots = model.list_entity_robots(user.username)
return { return {
'robots': [robot_view(name, password) for name, password in robots] 'robots': [robot_view(name, password) for name, password in robots]
@ -32,24 +30,20 @@ class UserRobotList(ApiResource):
@resource('/v1/user/robots/<robot_shortname>') @resource('/v1/user/robots/<robot_shortname>')
class UserRobot(ApiResource): class UserRobot(ApiResource):
""" Resource for managing a user's robots. """ """ Resource for managing a user's robots. """
@require_user_admin
@nickname('createUserRobot') @nickname('createUserRobot')
def put(self, robot_shortname): def put(self, robot_shortname):
""" Create a new user robot with the specified name. """ """ Create a new user robot with the specified name. """
parent = get_authenticated_user() parent = get_authenticated_user()
if not parent:
raise Unauthorized()
robot, password = model.create_robot(robot_shortname, parent) robot, password = model.create_robot(robot_shortname, parent)
log_action('create_robot', parent.username, {'robot': robot_shortname}) log_action('create_robot', parent.username, {'robot': robot_shortname})
return robot_view(robot.username, password), 201 return robot_view(robot.username, password), 201
@require_user_admin
@nickname('deleteUserRobot') @nickname('deleteUserRobot')
def delete(self, robot_shortname): def delete(self, robot_shortname):
""" Delete an existing robot. """ """ Delete an existing robot. """
parent = get_authenticated_user() parent = get_authenticated_user()
if not parent:
raise Unauthorized()
model.delete_robot(format_robot_username(parent.username, robot_shortname)) model.delete_robot(format_robot_username(parent.username, robot_shortname))
log_action('delete_robot', parent.username, {'robot': robot_shortname}) log_action('delete_robot', parent.username, {'robot': robot_shortname})
return 'Deleted', 204 return 'Deleted', 204

View file

@ -14,7 +14,7 @@ from endpoints.common import start_build
from endpoints.trigger import (BuildTrigger, TriggerDeactivationException, from endpoints.trigger import (BuildTrigger, TriggerDeactivationException,
TriggerActivationException, EmptyRepositoryException) TriggerActivationException, EmptyRepositoryException)
from data import model from data import model
from auth.permissions import UserPermission from auth.permissions import UserAdminPermission
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -103,7 +103,7 @@ class BuildTriggerSubdirs(RepositoryParamResource):
raise NotFound() raise NotFound()
handler = BuildTrigger.get_trigger_for_service(trigger.service.name) handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
user_permission = UserPermission(trigger.connected_user.username) user_permission = UserAdminPermission(trigger.connected_user.username)
if user_permission.can(): if user_permission.can():
new_config_dict = request.get_json() new_config_dict = request.get_json()

View file

@ -7,7 +7,8 @@ from flask.ext.principal import identity_changed, AnonymousIdentity
from app import app from app import app
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error, from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
log_action, internal_only, NotFound, Unauthorized) log_action, internal_only, NotFound, Unauthorized, require_user_admin,
require_user_read, InvalidToken)
from endpoints.api.subscribe import subscribe from endpoints.api.subscribe import subscribe
from endpoints.common import common_login from endpoints.common import common_login
from data import model from data import model
@ -107,24 +108,23 @@ class User(ApiResource):
}, },
} }
@require_user_read
@nickname('getLoggedInUser') @nickname('getLoggedInUser')
def get(self): def get(self):
""" Get user information for the authenticated user. """ """ Get user information for the authenticated user. """
user = get_authenticated_user() user = get_authenticated_user()
if user is None or user.organization: if user.organization:
return {'anonymous': True} raise InvalidToken('User must not be an organization.')
return user_view(user) return user_view(user)
@require_user_admin
@nickname('changeUserDetails') @nickname('changeUserDetails')
@internal_only @internal_only
@validate_json_request('UpdateUser') @validate_json_request('UpdateUser')
def put(self): def put(self):
""" Update a users details such as password or email. """ """ Update a users details such as password or email. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
user_data = request.get_json() user_data = request.get_json()
try: try:
@ -173,18 +173,15 @@ class User(ApiResource):
except model.DataModelException as ex: except model.DataModelException as ex:
raise request_error(exception=ex) raise request_error(exception=ex)
@resource('/v1/user/private') @resource('/v1/user/private')
class PrivateRepositories(ApiResource): class PrivateRepositories(ApiResource):
""" Operations dealing with the available count of private repositories. """ """ Operations dealing with the available count of private repositories. """
@require_user_admin
@nickname('getUserPrivateAllowed') @nickname('getUserPrivateAllowed')
def get(self): def get(self):
""" Get the number of private repos this user has, and whether they are allowed to create more. """ Get the number of private repos this user has, and whether they are allowed to create more.
""" """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
private_repos = model.get_private_repo_count(user.username) private_repos = model.get_private_repo_count(user.username)
repos_allowed = 0 repos_allowed = 0
@ -252,14 +249,12 @@ class ConvertToOrganization(ApiResource):
}, },
} }
@require_user_admin
@nickname('convertUserToOrganization') @nickname('convertUserToOrganization')
@validate_json_request('ConvertUser') @validate_json_request('ConvertUser')
def post(self): def post(self):
""" Convert the user to an organization. """ """ Convert the user to an organization. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
convert_data = request.get_json() convert_data = request.get_json()
# Ensure that the new admin user is the not user being converted. # Ensure that the new admin user is the not user being converted.

View file

@ -63,7 +63,7 @@ class _LoginWrappedDBUser(UserMixin):
def common_login(db_user): def common_login(db_user):
if login_user(_LoginWrappedDBUser(db_user.username, db_user)): if login_user(_LoginWrappedDBUser(db_user.username, db_user)):
logger.debug('Successfully signed in as: %s' % db_user.username) logger.debug('Successfully signed in as: %s' % db_user.username)
new_identity = QuayDeferredPermissionUser(db_user.username, 'username') new_identity = QuayDeferredPermissionUser(db_user.username, 'username', {'direct_user_login'})
identity_changed.send(app, identity=new_identity) identity_changed.send(app, identity=new_identity)
return True return True
else: else:

View file

@ -6,16 +6,15 @@ from flask import request, make_response, jsonify, session, Blueprint
from functools import wraps from functools import wraps
from collections import OrderedDict from collections import OrderedDict
from data import model, userevent from data import model
from data.queue import webhook_queue from data.queue import webhook_queue
from app import mixpanel, app from app import mixpanel, app
from auth.auth import process_auth from auth.auth import process_auth
from auth.auth_context import get_authenticated_user, get_validated_token from auth.auth_context import get_authenticated_user, get_validated_token
from util.names import parse_repository_name from util.names import parse_repository_name
from util.email import send_confirmation_email from util.email import send_confirmation_email
from auth.permissions import (ModifyRepositoryPermission, UserPermission, from auth.permissions import (ModifyRepositoryPermission, UserAdminPermission,
ReadRepositoryPermission, ReadRepositoryPermission, CreateRepositoryPermission)
CreateRepositoryPermission)
from util.http import abort from util.http import abort
@ -131,7 +130,7 @@ def get_user():
@index.route('/users/<username>/', methods=['PUT']) @index.route('/users/<username>/', methods=['PUT'])
@process_auth @process_auth
def update_user(username): def update_user(username):
permission = UserPermission(username) permission = UserAdminPermission(username)
if permission.can(): if permission.can():
update_request = request.get_json() update_request = request.get_json()