Protect the search and repository list endpoints appropriately. Add more differentiating data to some need types. Remove the notification about password change from the user admin page. Select the dependent models for the visible repo list.
This commit is contained in:
parent
afb3a67b7b
commit
41cfadac23
7 changed files with 53 additions and 44 deletions
|
@ -15,10 +15,13 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_ResourceNeed = namedtuple('resource', ['type', 'namespace', 'name', 'role'])
|
_ResourceNeed = namedtuple('resource', ['type', 'namespace', 'name', 'role'])
|
||||||
_RepositoryNeed = partial(_ResourceNeed, 'repository')
|
_RepositoryNeed = partial(_ResourceNeed, 'repository')
|
||||||
_OrganizationNeed = namedtuple('organization', ['orgname', 'role'])
|
_NamespaceWideNeed = namedtuple('namespacewide', ['type', 'namespace', 'role'])
|
||||||
_OrganizationRepoNeed = namedtuple('organization', ['orgname', 'role'])
|
_OrganizationNeed = partial(_NamespaceWideNeed, 'organization')
|
||||||
_TeamNeed = namedtuple('orgteam', ['orgname', 'teamname', 'role'])
|
_OrganizationRepoNeed = partial(_NamespaceWideNeed, 'organizationrepo')
|
||||||
_UserNeed = namedtuple('user', ['username', 'role'])
|
_TeamTypeNeed = namedtuple('teamwideneed', ['type', 'orgname', 'teamname', 'role'])
|
||||||
|
_TeamNeed = partial(_TeamTypeNeed, 'orgteam')
|
||||||
|
_UserTypeNeed = namedtuple('userspecificneed', ['type', 'username', 'role'])
|
||||||
|
_UserNeed = partial(_UserTypeNeed, 'user')
|
||||||
|
|
||||||
|
|
||||||
REPO_ROLES = [None, 'read', 'write', 'admin']
|
REPO_ROLES = [None, 'read', 'write', 'admin']
|
||||||
|
@ -87,8 +90,8 @@ class QuayDeferredPermissionUser(Identity):
|
||||||
|
|
||||||
# Add the user specific permissions, only for non-oauth permission
|
# Add the user specific permissions, only for non-oauth permission
|
||||||
user_grant = _UserNeed(user_object.username, self._user_role_for_scopes('admin'))
|
user_grant = _UserNeed(user_object.username, self._user_role_for_scopes('admin'))
|
||||||
self.provides.add(user_grant)
|
|
||||||
logger.debug('User permission: {0}'.format(user_grant))
|
logger.debug('User permission: {0}'.format(user_grant))
|
||||||
|
self.provides.add(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'))
|
||||||
|
@ -97,22 +100,22 @@ class QuayDeferredPermissionUser(Identity):
|
||||||
|
|
||||||
# Org repo roles can differ for scopes
|
# Org repo roles can differ for scopes
|
||||||
user_repos = _OrganizationRepoNeed(user_object.username, self._repo_role_for_scopes('admin'))
|
user_repos = _OrganizationRepoNeed(user_object.username, self._repo_role_for_scopes('admin'))
|
||||||
logger.debug('User namespace permission: {0}'.format(user_repos))
|
logger.debug('User namespace repo permission: {0}'.format(user_repos))
|
||||||
self.provides.add(user_repos)
|
self.provides.add(user_repos)
|
||||||
|
|
||||||
# Add repository permissions
|
# Add repository permissions
|
||||||
for perm in model.get_all_user_permissions(user_object):
|
for perm in model.get_all_user_permissions(user_object):
|
||||||
grant = _RepositoryNeed(perm.repository.namespace, perm.repository.name,
|
repo_grant = _RepositoryNeed(perm.repository.namespace, perm.repository.name,
|
||||||
self._repo_role_for_scopes(perm.role.name))
|
self._repo_role_for_scopes(perm.role.name))
|
||||||
logger.debug('User added permission: {0}'.format(grant))
|
logger.debug('User added permission: {0}'.format(repo_grant))
|
||||||
self.provides.add(grant)
|
self.provides.add(repo_grant)
|
||||||
|
|
||||||
# Add namespace permissions derived
|
# Add namespace permissions derived
|
||||||
for team in model.get_org_wide_permissions(user_object):
|
for team in model.get_org_wide_permissions(user_object):
|
||||||
grant = _OrganizationNeed(team.organization.username,
|
team_org_grant = _OrganizationNeed(team.organization.username,
|
||||||
self._team_role_for_scopes(team.role.name))
|
self._team_role_for_scopes(team.role.name))
|
||||||
logger.debug('Organization team added permission: {0}'.format(grant))
|
logger.debug('Organization team added permission: {0}'.format(team_org_grant))
|
||||||
self.provides.add(grant)
|
self.provides.add(team_org_grant)
|
||||||
|
|
||||||
|
|
||||||
team_repo_role = TEAM_REPO_ROLES[team.role.name]
|
team_repo_role = TEAM_REPO_ROLES[team.role.name]
|
||||||
|
|
|
@ -555,9 +555,9 @@ def get_visible_repository_count(username=None, include_public=True,
|
||||||
|
|
||||||
def get_visible_repositories(username=None, include_public=True, page=None,
|
def get_visible_repositories(username=None, include_public=True, page=None,
|
||||||
limit=None, sort=False, namespace=None):
|
limit=None, sort=False, namespace=None):
|
||||||
query = _visible_repository_query(username=username,
|
query = _visible_repository_query(username=username, include_public=include_public, page=page,
|
||||||
include_public=include_public, page=page,
|
limit=limit, namespace=namespace,
|
||||||
limit=limit, namespace=namespace)
|
select_models=[Repository, Visibility])
|
||||||
|
|
||||||
if sort:
|
if sort:
|
||||||
query = query.order_by(Repository.description.desc())
|
query = query.order_by(Repository.description.desc())
|
||||||
|
@ -569,9 +569,9 @@ def get_visible_repositories(username=None, include_public=True, page=None,
|
||||||
|
|
||||||
|
|
||||||
def _visible_repository_query(username=None, include_public=True, limit=None,
|
def _visible_repository_query(username=None, include_public=True, limit=None,
|
||||||
page=None, namespace=None):
|
page=None, namespace=None, select_models=[]):
|
||||||
query = (Repository
|
query = (Repository
|
||||||
.select() # Note: We need to leave this blank for the get_count case. Otherwise, MySQL/RDS complains.
|
.select(*select_models) # Note: We need to leave this blank for the get_count case. Otherwise, MySQL/RDS complains.
|
||||||
.distinct()
|
.distinct()
|
||||||
.join(Visibility)
|
.join(Visibility)
|
||||||
.switch(Repository)
|
.switch(Repository)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from endpoints.api import (truthy_bool, format_date, nickname, log_action, valid
|
||||||
RepositoryParamResource, resource, query_param, parse_args, ApiResource,
|
RepositoryParamResource, resource, query_param, parse_args, ApiResource,
|
||||||
request_error, require_scope, Unauthorized, NotFound, InvalidRequest)
|
request_error, require_scope, Unauthorized, NotFound, InvalidRequest)
|
||||||
from auth.permissions import (ModifyRepositoryPermission, AdministerRepositoryPermission,
|
from auth.permissions import (ModifyRepositoryPermission, AdministerRepositoryPermission,
|
||||||
CreateRepositoryPermission)
|
CreateRepositoryPermission, ReadRepositoryPermission)
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
|
|
||||||
|
@ -132,7 +132,8 @@ class RepositoryList(ApiResource):
|
||||||
include_public=args['public'], sort=args['sort'],
|
include_public=args['public'], sort=args['sort'],
|
||||||
namespace=args['namespace'])
|
namespace=args['namespace'])
|
||||||
|
|
||||||
response['repositories'] = [repo_view(repo) for repo in repo_query]
|
response['repositories'] = [repo_view(repo) for repo in repo_query
|
||||||
|
if ReadRepositoryPermission(repo.namespace, repo.name).can()]
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, nickname, resource,
|
from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, nickname, resource,
|
||||||
require_scope)
|
require_scope)
|
||||||
from data import model
|
from data import model
|
||||||
from auth.permissions import OrganizationMemberPermission, ViewTeamPermission
|
from auth.permissions import (OrganizationMemberPermission, ViewTeamPermission,
|
||||||
|
ReadRepositoryPermission, UserAdminPermission)
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
|
|
||||||
|
@ -13,7 +14,6 @@ class EntitySearch(ApiResource):
|
||||||
@query_param('namespace', 'Namespace to use when querying for org entities.', type=str,
|
@query_param('namespace', 'Namespace to use when querying for org entities.', type=str,
|
||||||
default='')
|
default='')
|
||||||
@query_param('includeTeams', 'Whether to include team names.', type=truthy_bool, default=False)
|
@query_param('includeTeams', 'Whether to include team names.', type=truthy_bool, default=False)
|
||||||
@require_scope(scopes.READ_USER)
|
|
||||||
@nickname('getMatchingEntities')
|
@nickname('getMatchingEntities')
|
||||||
def get(self, args, prefix):
|
def get(self, args, prefix):
|
||||||
""" Get a list of entities that match the specified prefix. """
|
""" Get a list of entities that match the specified prefix. """
|
||||||
|
@ -38,6 +38,9 @@ class EntitySearch(ApiResource):
|
||||||
# namespace name was a user
|
# namespace name was a user
|
||||||
user = get_authenticated_user()
|
user = get_authenticated_user()
|
||||||
if user and user.username == namespace_name:
|
if user and user.username == namespace_name:
|
||||||
|
# Check if there is admin user permissions (login only)
|
||||||
|
admin_permission = UserAdminPermission(user.username)
|
||||||
|
if admin_permission.can():
|
||||||
robot_namespace = namespace_name
|
robot_namespace = namespace_name
|
||||||
|
|
||||||
users = model.get_matching_users(prefix, robot_namespace, organization)
|
users = model.get_matching_users(prefix, robot_namespace, organization)
|
||||||
|
@ -87,7 +90,7 @@ class FindRepositories(ApiResource):
|
||||||
""" Resource for finding repositories. """
|
""" Resource for finding repositories. """
|
||||||
@parse_args
|
@parse_args
|
||||||
@query_param('query', 'The prefix to use when querying for repositories.', type=str, default='')
|
@query_param('query', 'The prefix to use when querying for repositories.', type=str, default='')
|
||||||
@require_scope(scopes.READ_USER)
|
@require_scope(scopes.READ_REPO)
|
||||||
@nickname('findRepos')
|
@nickname('findRepos')
|
||||||
def get(self, args):
|
def get(self, args):
|
||||||
""" Get a list of repositories that match the specified prefix query. """
|
""" Get a list of repositories that match the specified prefix query. """
|
||||||
|
@ -101,10 +104,12 @@ class FindRepositories(ApiResource):
|
||||||
}
|
}
|
||||||
|
|
||||||
username = None
|
username = None
|
||||||
if get_authenticated_user() is not None:
|
user = get_authenticated_user()
|
||||||
username = get_authenticated_user().username
|
if user is not None:
|
||||||
|
username = user.username
|
||||||
|
|
||||||
matching = model.get_matching_repositories(prefix, username)
|
matching = model.get_matching_repositories(prefix, username)
|
||||||
return {
|
return {
|
||||||
'repositories': [repo_view(repo) for repo in matching]
|
'repositories': [repo_view(repo) for repo in matching
|
||||||
|
if ReadRepositoryPermission(repo.namespace, repo.name).can()]
|
||||||
}
|
}
|
|
@ -14,7 +14,8 @@ 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
|
||||||
from data.plans import get_plan
|
from data.plans import get_plan
|
||||||
from auth.permissions import AdministerOrganizationPermission, CreateRepositoryPermission
|
from auth.permissions import (AdministerOrganizationPermission, CreateRepositoryPermission,
|
||||||
|
UserAdminPermission)
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
from util.gravatar import compute_hash
|
from util.gravatar import compute_hash
|
||||||
|
@ -46,19 +47,25 @@ def user_view(user):
|
||||||
|
|
||||||
logins = model.list_federated_logins(user)
|
logins = model.list_federated_logins(user)
|
||||||
|
|
||||||
return {
|
user_response = {
|
||||||
'verified': user.verified,
|
'verified': user.verified,
|
||||||
'anonymous': False,
|
'anonymous': False,
|
||||||
'username': user.username,
|
'username': user.username,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'gravatar': compute_hash(user.email),
|
'gravatar': compute_hash(user.email),
|
||||||
'askForPassword': user.password_hash is None,
|
}
|
||||||
|
|
||||||
|
user_admin = UserAdminPermission(user.username)
|
||||||
|
if user_admin.can():
|
||||||
|
user_response.update({
|
||||||
'organizations': [org_view(o) for o in organizations],
|
'organizations': [org_view(o) for o in organizations],
|
||||||
'logins': [login_view(login) for login in logins],
|
'logins': [login_view(login) for login in logins],
|
||||||
'can_create_repo': True,
|
'can_create_repo': True,
|
||||||
'invoice_email': user.invoice_email,
|
'invoice_email': user.invoice_email,
|
||||||
'preferred_namespace': not (user.stripe_id is None)
|
'preferred_namespace': not (user.stripe_id is None),
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return user_response
|
||||||
|
|
||||||
|
|
||||||
def notification_view(notification):
|
def notification_view(notification):
|
||||||
|
@ -119,7 +126,7 @@ class User(ApiResource):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@require_scope(scopes.READ_USER)
|
@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. """
|
||||||
|
|
|
@ -1602,7 +1602,6 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
||||||
}
|
}
|
||||||
|
|
||||||
UserService.updateUserIn($scope, function(user) {
|
UserService.updateUserIn($scope, function(user) {
|
||||||
$scope.askForPassword = user.askForPassword;
|
|
||||||
$scope.cuser = jQuery.extend({}, user);
|
$scope.cuser = jQuery.extend({}, user);
|
||||||
|
|
||||||
for (var i = 0; i < $scope.cuser.logins.length; i++) {
|
for (var i = 0; i < $scope.cuser.logins.length; i++) {
|
||||||
|
|
|
@ -16,12 +16,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-show="askForPassword">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="alert alert-warning">Your account does not currently have a password. You will need to create a password before you will be able to <strong>push</strong> or <strong>pull</strong> repositories.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Side tabs -->
|
<!-- Side tabs -->
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
|
|
Reference in a new issue