Add a user info scope and thread it through the code. Protect the org modification API.
This commit is contained in:
parent
89556172d5
commit
64071b9e8e
13 changed files with 144 additions and 115 deletions
|
@ -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.')
|
||||||
|
|
|
@ -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':
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Reference in a new issue