Merge branch 'koh'
Conflicts: auth/scopes.py requirements-nover.txt requirements.txt static/css/quay.css static/directives/namespace-selector.html static/js/app.js static/partials/manage-application.html templates/oauthorize.html
This commit is contained in:
commit
f3259c862b
57 changed files with 537 additions and 141 deletions
|
@ -1,11 +1,10 @@
|
|||
import logging
|
||||
import json
|
||||
import datetime
|
||||
|
||||
from app import app
|
||||
from flask import Blueprint, request, make_response, jsonify, session
|
||||
from flask.ext.restful import Resource, abort, Api, reqparse
|
||||
from flask.ext.restful.utils.cors import crossdomain
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from calendar import timegm
|
||||
from email.utils import formatdate
|
||||
from functools import partial, wraps
|
||||
|
@ -52,6 +51,10 @@ class InvalidRequest(ApiException):
|
|||
def __init__(self, error_description, payload=None):
|
||||
ApiException.__init__(self, 'invalid_request', 400, error_description, payload)
|
||||
|
||||
class InvalidResponse(ApiException):
|
||||
def __init__(self, error_description, payload=None):
|
||||
ApiException.__init__(self, 'invalid_response', 400, error_description, payload)
|
||||
|
||||
|
||||
class InvalidToken(ApiException):
|
||||
def __init__(self, error_description, payload=None):
|
||||
|
@ -170,6 +173,9 @@ internal_only = add_method_metadata('internal', True)
|
|||
|
||||
def path_param(name, description):
|
||||
def add_param(func):
|
||||
if not func:
|
||||
return func
|
||||
|
||||
if '__api_path_params' not in dir(func):
|
||||
func.__api_path_params = {}
|
||||
func.__api_path_params[name] = {
|
||||
|
@ -346,6 +352,25 @@ def log_action(kind, user_or_orgname, metadata=None, repo=None):
|
|||
metadata=metadata, repository=repo)
|
||||
|
||||
|
||||
def define_json_response(schema_name):
|
||||
def wrapper(func):
|
||||
@add_method_metadata('response_schema', schema_name)
|
||||
@wraps(func)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
schema = self.schemas[schema_name]
|
||||
resp = func(self, *args, **kwargs)
|
||||
|
||||
if app.config['TESTING']:
|
||||
try:
|
||||
validate(resp, schema)
|
||||
except ValidationError as ex:
|
||||
raise InvalidResponse(ex.message)
|
||||
|
||||
return resp
|
||||
return wrapped
|
||||
return wrapper
|
||||
|
||||
|
||||
import endpoints.api.billing
|
||||
import endpoints.api.build
|
||||
import endpoints.api.discovery
|
||||
|
|
|
@ -4,10 +4,11 @@ from flask import request
|
|||
from app import billing
|
||||
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action,
|
||||
related_user_resource, internal_only, Unauthorized, NotFound,
|
||||
require_user_admin, show_if, hide_if, abort)
|
||||
require_user_admin, show_if, hide_if, path_param, require_scope, abort)
|
||||
from endpoints.api.subscribe import subscribe, subscription_view
|
||||
from auth.permissions import AdministerOrganizationPermission
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from data import model
|
||||
from data.billing import PLANS
|
||||
|
||||
|
@ -149,6 +150,7 @@ class UserCard(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/card')
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@internal_only
|
||||
@related_user_resource(UserCard)
|
||||
@show_if(features.BILLING)
|
||||
|
@ -171,6 +173,7 @@ class OrganizationCard(ApiResource):
|
|||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrgCard')
|
||||
def get(self, orgname):
|
||||
""" Get the organization's credit card. """
|
||||
|
@ -259,6 +262,7 @@ class UserPlan(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/plan')
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@internal_only
|
||||
@related_user_resource(UserPlan)
|
||||
@show_if(features.BILLING)
|
||||
|
@ -285,6 +289,7 @@ class OrganizationPlan(ApiResource):
|
|||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('updateOrgSubscription')
|
||||
@validate_json_request('OrgSubscription')
|
||||
def put(self, orgname):
|
||||
|
@ -299,6 +304,7 @@ class OrganizationPlan(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrgSubscription')
|
||||
def get(self, orgname):
|
||||
""" Fetch any existing subscription for the org. """
|
||||
|
@ -343,11 +349,12 @@ class UserInvoiceList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/invoices')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@related_user_resource(UserInvoiceList)
|
||||
@show_if(features.BILLING)
|
||||
class OrgnaizationInvoiceList(ApiResource):
|
||||
""" Resource for listing an orgnaization's invoices. """
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('listOrgInvoices')
|
||||
def get(self, orgname):
|
||||
""" List the invoices for the specified orgnaization. """
|
||||
|
|
|
@ -8,7 +8,8 @@ from flask import request, redirect
|
|||
from app import app, userfiles as user_files, build_logs, log_archive
|
||||
from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource,
|
||||
require_repo_read, require_repo_write, validate_json_request,
|
||||
ApiResource, internal_only, format_date, api, Unauthorized, NotFound)
|
||||
ApiResource, internal_only, format_date, api, Unauthorized, NotFound,
|
||||
path_param)
|
||||
from endpoints.common import start_build
|
||||
from endpoints.trigger import BuildTrigger
|
||||
from data import model, database
|
||||
|
@ -95,6 +96,7 @@ def build_status_view(build_obj, can_write=False):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/build/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryBuildList(RepositoryParamResource):
|
||||
""" Resource related to creating and listing repository builds. """
|
||||
schemas = {
|
||||
|
@ -199,6 +201,8 @@ class RepositoryBuildList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/build/<build_uuid>/status')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('build_uuid', 'The UUID of the build')
|
||||
class RepositoryBuildStatus(RepositoryParamResource):
|
||||
""" Resource for dealing with repository build status. """
|
||||
@require_repo_read
|
||||
|
@ -215,6 +219,8 @@ class RepositoryBuildStatus(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/build/<build_uuid>/logs')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('build_uuid', 'The UUID of the build')
|
||||
class RepositoryBuildLogs(RepositoryParamResource):
|
||||
""" Resource for loading repository build logs. """
|
||||
@require_repo_write
|
||||
|
|
|
@ -94,13 +94,19 @@ def swagger_route_data(include_internal=False, compact=False):
|
|||
|
||||
new_operation = {
|
||||
'method': method_name,
|
||||
'nickname': method_metadata(method, 'nickname')
|
||||
'nickname': method_metadata(method, 'nickname') or '(unnamed)'
|
||||
}
|
||||
|
||||
if not compact:
|
||||
response_type = 'void'
|
||||
res_schema_name = method_metadata(method, 'response_schema')
|
||||
if res_schema_name:
|
||||
models[res_schema_name] = view_class.schemas[res_schema_name]
|
||||
response_type = res_schema_name
|
||||
|
||||
new_operation.update({
|
||||
'type': 'void',
|
||||
'summary': method.__doc__ if method.__doc__ else '',
|
||||
'type': response_type,
|
||||
'summary': method.__doc__.strip() if method.__doc__ else '',
|
||||
'parameters': parameters,
|
||||
})
|
||||
|
||||
|
@ -134,7 +140,7 @@ def swagger_route_data(include_internal=False, compact=False):
|
|||
swagger_path = PARAM_REGEX.sub(r'{\2}', rule.rule)
|
||||
new_resource = {
|
||||
'path': swagger_path,
|
||||
'description': view_class.__doc__ if view_class.__doc__ else "",
|
||||
'description': view_class.__doc__.strip() if view_class.__doc__ else "",
|
||||
'operations': operations,
|
||||
'name': fully_qualified_name(view_class),
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ from collections import defaultdict
|
|||
|
||||
from app import storage as store
|
||||
from endpoints.api import (resource, nickname, require_repo_read, RepositoryParamResource,
|
||||
format_date, NotFound)
|
||||
format_date, NotFound, path_param)
|
||||
from data import model
|
||||
from util.cache import cache_control_flask_restful
|
||||
|
||||
|
@ -40,6 +40,7 @@ def image_view(image, image_map):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/image/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryImageList(RepositoryParamResource):
|
||||
""" Resource for listing repository images. """
|
||||
@require_repo_read
|
||||
|
@ -67,6 +68,8 @@ class RepositoryImageList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/image/<image_id>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('image_id', 'The Docker image ID')
|
||||
class RepositoryImage(RepositoryParamResource):
|
||||
""" Resource for handling repository images. """
|
||||
@require_repo_read
|
||||
|
@ -86,6 +89,8 @@ class RepositoryImage(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/image/<image_id>/changes')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('image_id', 'The Docker image ID')
|
||||
class RepositoryImageChanges(RepositoryParamResource):
|
||||
""" Resource for handling repository image change lists. """
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@ from datetime import datetime, timedelta
|
|||
from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args,
|
||||
RepositoryParamResource, require_repo_admin, related_user_resource,
|
||||
format_date, Unauthorized, NotFound, require_user_admin,
|
||||
internal_only)
|
||||
internal_only, path_param, require_scope)
|
||||
from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from data import model
|
||||
from auth import scopes
|
||||
|
||||
|
||||
def log_view(log):
|
||||
|
@ -63,7 +64,7 @@ def get_logs(start_time, end_time, performer_name=None, repository=None, namespa
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/logs')
|
||||
@internal_only
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryLogs(RepositoryParamResource):
|
||||
""" Resource for fetching logs for the specific repository. """
|
||||
@require_repo_admin
|
||||
|
@ -103,7 +104,7 @@ class UserLogs(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/logs')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@related_user_resource(UserLogs)
|
||||
class OrgLogs(ApiResource):
|
||||
""" Resource for fetching logs for the entire organization. """
|
||||
|
@ -112,6 +113,7 @@ class OrgLogs(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.ORG_ADMIN)
|
||||
def get(self, args, orgname):
|
||||
""" List the logs for the specified organization. """
|
||||
permission = AdministerOrganizationPermission(orgname)
|
||||
|
|
|
@ -2,18 +2,19 @@ import logging
|
|||
|
||||
from flask import request
|
||||
|
||||
from app import billing as stripe
|
||||
from app import billing as stripe, avatar
|
||||
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
|
||||
related_user_resource, internal_only, Unauthorized, NotFound,
|
||||
require_user_admin, log_action, show_if)
|
||||
require_user_admin, log_action, show_if, path_param,
|
||||
require_scope)
|
||||
from endpoints.api.team import team_view
|
||||
from endpoints.api.user import User, PrivateRepositories
|
||||
from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission,
|
||||
CreateRepositoryPermission)
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from data import model
|
||||
from data.billing import get_plan
|
||||
from util.gravatar import compute_hash
|
||||
|
||||
import features
|
||||
|
||||
|
@ -28,7 +29,7 @@ def org_view(o, teams):
|
|||
view = {
|
||||
'name': o.username,
|
||||
'email': o.email if is_admin else '',
|
||||
'gravatar': compute_hash(o.email),
|
||||
'avatar': avatar.compute_hash(o.email, name=o.username),
|
||||
'teams': {t.name : team_view(o.username, t) for t in teams},
|
||||
'is_admin': is_admin
|
||||
}
|
||||
|
@ -97,7 +98,7 @@ class OrganizationList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@related_user_resource(User)
|
||||
class Organization(ApiResource):
|
||||
""" Resource for managing organizations. """
|
||||
|
@ -118,6 +119,8 @@ class Organization(ApiResource):
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganization')
|
||||
def get(self, orgname):
|
||||
""" Get the details for the specified organization """
|
||||
|
@ -133,6 +136,7 @@ class Organization(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('changeOrganizationDetails')
|
||||
@validate_json_request('UpdateOrg')
|
||||
def put(self, orgname):
|
||||
|
@ -163,11 +167,14 @@ class Organization(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/private')
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@internal_only
|
||||
@related_user_resource(PrivateRepositories)
|
||||
@show_if(features.BILLING)
|
||||
class OrgPrivateRepositories(ApiResource):
|
||||
""" Custom verb to compute whether additional private repositories are available. """
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationPrivateAllowed')
|
||||
def get(self, orgname):
|
||||
""" Return whether or not this org is allowed to create new private repositories. """
|
||||
|
@ -199,9 +206,11 @@ class OrgPrivateRepositories(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/members')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
class OrgnaizationMemberList(ApiResource):
|
||||
""" Resource for listing the members of an organization. """
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationMembers')
|
||||
def get(self, orgname):
|
||||
""" List the members of the specified organization. """
|
||||
|
@ -232,9 +241,12 @@ class OrgnaizationMemberList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/members/<membername>')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('membername', 'The username of the organization member')
|
||||
class OrganizationMember(ApiResource):
|
||||
""" Resource for managing individual organization members. """
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationMember')
|
||||
def get(self, orgname, membername):
|
||||
""" Get information on the specific orgnaization member. """
|
||||
|
@ -265,8 +277,10 @@ class OrganizationMember(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/app/<client_id>')
|
||||
@path_param('client_id', 'The OAuth client ID')
|
||||
class ApplicationInformation(ApiResource):
|
||||
""" Resource that returns public information about a registered application. """
|
||||
|
||||
@nickname('getApplicationInformation')
|
||||
def get(self, client_id):
|
||||
""" Get information on the specified application. """
|
||||
|
@ -274,14 +288,16 @@ class ApplicationInformation(ApiResource):
|
|||
if not application:
|
||||
raise NotFound()
|
||||
|
||||
org_hash = compute_hash(application.organization.email)
|
||||
gravatar = compute_hash(application.gravatar_email) if application.gravatar_email else org_hash
|
||||
org_hash = avatar.compute_hash(application.organization.email,
|
||||
name=application.organization.username)
|
||||
app_hash = (avatar.compute_hash(application.avatar_email, name=application.name) if
|
||||
application.avatar_email else org_hash)
|
||||
|
||||
return {
|
||||
'name': application.name,
|
||||
'description': application.description,
|
||||
'uri': application.application_uri,
|
||||
'gravatar': gravatar,
|
||||
'avatar': app_hash,
|
||||
'organization': org_view(application.organization, [])
|
||||
}
|
||||
|
||||
|
@ -297,12 +313,12 @@ def app_view(application):
|
|||
'client_id': application.client_id,
|
||||
'client_secret': application.client_secret if is_admin else None,
|
||||
'redirect_uri': application.redirect_uri if is_admin else None,
|
||||
'gravatar_email': application.gravatar_email if is_admin else None,
|
||||
'avatar_email': application.avatar_email if is_admin else None,
|
||||
}
|
||||
|
||||
|
||||
@resource('/v1/organization/<orgname>/applications')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
class OrganizationApplications(ApiResource):
|
||||
""" Resource for managing applications defined by an organizations. """
|
||||
schemas = {
|
||||
|
@ -330,15 +346,15 @@ class OrganizationApplications(ApiResource):
|
|||
'type': 'string',
|
||||
'description': 'The human-readable description for the application',
|
||||
},
|
||||
'gravatar_email': {
|
||||
'avatar_email': {
|
||||
'type': 'string',
|
||||
'description': 'The e-mail address of the gravatar to use for the application',
|
||||
'description': 'The e-mail address of the avatar to use for the application',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationApplications')
|
||||
def get(self, orgname):
|
||||
""" List the applications for the specified organization """
|
||||
|
@ -354,6 +370,7 @@ class OrganizationApplications(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('createOrganizationApplication')
|
||||
@validate_json_request('NewApp')
|
||||
def post(self, orgname):
|
||||
|
@ -371,7 +388,7 @@ class OrganizationApplications(ApiResource):
|
|||
app_data.get('application_uri', ''),
|
||||
app_data.get('redirect_uri', ''),
|
||||
description = app_data.get('description', ''),
|
||||
gravatar_email = app_data.get('gravatar_email', None),)
|
||||
avatar_email = app_data.get('avatar_email', None),)
|
||||
|
||||
|
||||
app_data.update({
|
||||
|
@ -386,7 +403,8 @@ class OrganizationApplications(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/applications/<client_id>')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('client_id', 'The OAuth client ID')
|
||||
class OrganizationApplicationResource(ApiResource):
|
||||
""" Resource for managing an application defined by an organizations. """
|
||||
schemas = {
|
||||
|
@ -416,14 +434,15 @@ class OrganizationApplicationResource(ApiResource):
|
|||
'type': 'string',
|
||||
'description': 'The human-readable description for the application',
|
||||
},
|
||||
'gravatar_email': {
|
||||
'avatar_email': {
|
||||
'type': 'string',
|
||||
'description': 'The e-mail address of the gravatar to use for the application',
|
||||
'description': 'The e-mail address of the avatar to use for the application',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationApplication')
|
||||
def get(self, orgname, client_id):
|
||||
""" Retrieves the application with the specified client_id under the specified organization """
|
||||
|
@ -442,6 +461,7 @@ class OrganizationApplicationResource(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('updateOrganizationApplication')
|
||||
@validate_json_request('UpdateApp')
|
||||
def put(self, orgname, client_id):
|
||||
|
@ -462,7 +482,7 @@ class OrganizationApplicationResource(ApiResource):
|
|||
application.application_uri = app_data['application_uri']
|
||||
application.redirect_uri = app_data['redirect_uri']
|
||||
application.description = app_data.get('description', '')
|
||||
application.gravatar_email = app_data.get('gravatar_email', None)
|
||||
application.avatar_email = app_data.get('avatar_email', None)
|
||||
application.save()
|
||||
|
||||
app_data.update({
|
||||
|
@ -475,7 +495,7 @@ class OrganizationApplicationResource(ApiResource):
|
|||
return app_view(application)
|
||||
raise Unauthorized()
|
||||
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('deleteOrganizationApplication')
|
||||
def delete(self, orgname, client_id):
|
||||
""" Deletes the application under this organization. """
|
||||
|
@ -498,6 +518,8 @@ class OrganizationApplicationResource(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/applications/<client_id>/resetclientsecret')
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('client_id', 'The OAuth client ID')
|
||||
@internal_only
|
||||
class OrganizationApplicationResetClientSecret(ApiResource):
|
||||
""" Custom verb for resetting the client secret of an application. """
|
||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
|||
from flask import request
|
||||
|
||||
from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource,
|
||||
log_action, request_error, validate_json_request)
|
||||
log_action, request_error, validate_json_request, path_param)
|
||||
from data import model
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ def wrap_role_view_org(role_json, user, org_members):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/team/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryTeamPermissionList(RepositoryParamResource):
|
||||
""" Resource for repository team permissions. """
|
||||
@require_repo_admin
|
||||
|
@ -41,6 +42,7 @@ class RepositoryTeamPermissionList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/user/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryUserPermissionList(RepositoryParamResource):
|
||||
""" Resource for repository user permissions. """
|
||||
@require_repo_admin
|
||||
|
@ -80,6 +82,8 @@ class RepositoryUserPermissionList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/user/<username>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('username', 'The username of the user to which the permission applies')
|
||||
class RepositoryUserPermission(RepositoryParamResource):
|
||||
""" Resource for managing individual user permissions. """
|
||||
schemas = {
|
||||
|
@ -175,6 +179,8 @@ class RepositoryUserPermission(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/team/<teamname>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('teamname', 'The name of the team to which the permission applies')
|
||||
class RepositoryTeamPermission(RepositoryParamResource):
|
||||
""" Resource for managing individual team permissions. """
|
||||
schemas = {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from flask import request
|
||||
|
||||
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
|
||||
log_action, Unauthorized, NotFound, internal_only)
|
||||
log_action, Unauthorized, NotFound, internal_only, path_param,
|
||||
require_scope)
|
||||
from auth.permissions import AdministerOrganizationPermission
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from data import model
|
||||
|
||||
|
||||
|
@ -54,7 +56,7 @@ def log_prototype_action(action_kind, orgname, prototype, **kwargs):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/prototypes')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
class PermissionPrototypeList(ApiResource):
|
||||
""" Resource for listing and creating permission prototypes. """
|
||||
schemas = {
|
||||
|
@ -115,6 +117,7 @@ class PermissionPrototypeList(ApiResource):
|
|||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationPrototypePermissions')
|
||||
def get(self, orgname):
|
||||
""" List the existing prototypes for this organization. """
|
||||
|
@ -131,6 +134,7 @@ class PermissionPrototypeList(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('createOrganizationPrototypePermission')
|
||||
@validate_json_request('NewPrototype')
|
||||
def post(self, orgname):
|
||||
|
@ -179,7 +183,8 @@ class PermissionPrototypeList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/prototypes/<prototypeid>')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('prototypeid', 'The ID of the prototype')
|
||||
class PermissionPrototype(ApiResource):
|
||||
""" Resource for managingin individual permission prototypes. """
|
||||
schemas = {
|
||||
|
@ -204,6 +209,7 @@ class PermissionPrototype(ApiResource):
|
|||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('deleteOrganizationPrototypePermission')
|
||||
def delete(self, orgname, prototypeid):
|
||||
""" Delete an existing permission prototype. """
|
||||
|
@ -224,6 +230,7 @@ class PermissionPrototype(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('updateOrganizationPrototypePermission')
|
||||
@validate_json_request('PrototypeUpdate')
|
||||
def put(self, orgname, prototypeid):
|
||||
|
|
|
@ -4,7 +4,7 @@ from flask import request, abort
|
|||
|
||||
from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource,
|
||||
log_action, validate_json_request, NotFound, internal_only,
|
||||
show_if)
|
||||
path_param, show_if)
|
||||
|
||||
from app import tf
|
||||
from data import model
|
||||
|
@ -28,6 +28,8 @@ def record_view(record):
|
|||
@internal_only
|
||||
@show_if(features.MAILING)
|
||||
@resource('/v1/repository/<repopath:repository>/authorizedemail/<email>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('email', 'The e-mail address')
|
||||
class RepositoryAuthorizedEmail(RepositoryParamResource):
|
||||
""" Resource for checking and authorizing e-mail addresses to receive repo notifications. """
|
||||
@require_repo_admin
|
||||
|
|
|
@ -7,7 +7,9 @@ from data import model
|
|||
from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request,
|
||||
require_repo_read, require_repo_write, require_repo_admin,
|
||||
RepositoryParamResource, resource, query_param, parse_args, ApiResource,
|
||||
request_error, require_scope, Unauthorized, NotFound, InvalidRequest)
|
||||
request_error, require_scope, Unauthorized, NotFound, InvalidRequest,
|
||||
path_param)
|
||||
|
||||
from auth.permissions import (ModifyRepositoryPermission, AdministerRepositoryPermission,
|
||||
CreateRepositoryPermission, ReadRepositoryPermission)
|
||||
from auth.auth_context import get_authenticated_user
|
||||
|
@ -140,6 +142,7 @@ class RepositoryList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class Repository(RepositoryParamResource):
|
||||
"""Operations for managing a specific repository."""
|
||||
schemas = {
|
||||
|
@ -232,6 +235,7 @@ class Repository(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/changevisibility')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryVisibility(RepositoryParamResource):
|
||||
""" Custom verb for changing the visibility of the repository. """
|
||||
schemas = {
|
||||
|
|
|
@ -4,7 +4,8 @@ from flask import request, abort
|
|||
|
||||
from app import notification_queue
|
||||
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
||||
log_action, validate_json_request, api, NotFound, request_error)
|
||||
log_action, validate_json_request, api, NotFound, request_error,
|
||||
path_param)
|
||||
from endpoints.notificationevent import NotificationEvent
|
||||
from endpoints.notificationmethod import (NotificationMethod,
|
||||
CannotValidateNotificationMethodException)
|
||||
|
@ -28,6 +29,7 @@ def notification_view(notification):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/notification/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryNotificationList(RepositoryParamResource):
|
||||
""" Resource for dealing with listing and creating notifications on a repository. """
|
||||
schemas = {
|
||||
|
@ -95,6 +97,8 @@ class RepositoryNotificationList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/notification/<uuid>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('uuid', 'The UUID of the notification')
|
||||
class RepositoryNotification(RepositoryParamResource):
|
||||
""" Resource for dealing with specific notifications. """
|
||||
@require_repo_admin
|
||||
|
@ -126,6 +130,8 @@ class RepositoryNotification(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/notification/<uuid>/test')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('uuid', 'The UUID of the notification')
|
||||
class TestRepositoryNotification(RepositoryParamResource):
|
||||
""" Resource for queuing a test of a notification. """
|
||||
@require_repo_admin
|
||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
|||
from flask import request
|
||||
|
||||
from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource,
|
||||
log_action, validate_json_request, NotFound)
|
||||
log_action, validate_json_request, NotFound, path_param)
|
||||
from data import model
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ def token_view(token_obj):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/tokens/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryTokenList(RepositoryParamResource):
|
||||
""" Resource for creating and listing repository tokens. """
|
||||
schemas = {
|
||||
|
@ -66,6 +67,8 @@ class RepositoryTokenList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/tokens/<code>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('code', 'The token code')
|
||||
class RepositoryToken(RepositoryParamResource):
|
||||
""" Resource for managing individual tokens. """
|
||||
schemas = {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, nickname, resource,
|
||||
require_scope)
|
||||
require_scope, path_param)
|
||||
from data import model
|
||||
from auth.permissions import (OrganizationMemberPermission, ViewTeamPermission,
|
||||
ReadRepositoryPermission, UserAdminPermission,
|
||||
AdministerOrganizationPermission)
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from util.gravatar import compute_hash
|
||||
from app import avatar
|
||||
|
||||
|
||||
@resource('/v1/entities/<prefix>')
|
||||
class EntitySearch(ApiResource):
|
||||
""" Resource for searching entities. """
|
||||
@path_param('prefix', 'The prefix of the entities being looked up')
|
||||
@parse_args
|
||||
@query_param('namespace', 'Namespace to use when querying for org entities.', type=str,
|
||||
default='')
|
||||
|
@ -44,7 +45,7 @@ class EntitySearch(ApiResource):
|
|||
'name': namespace_name,
|
||||
'kind': 'org',
|
||||
'is_org_member': True,
|
||||
'gravatar': compute_hash(organization.email),
|
||||
'avatar': avatar.compute_hash(organization.email, name=organization.username),
|
||||
}]
|
||||
|
||||
except model.InvalidOrganizationException:
|
||||
|
|
|
@ -9,7 +9,7 @@ from flask import request
|
|||
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
|
||||
log_action, internal_only, NotFound, require_user_admin, format_date,
|
||||
InvalidToken, require_scope, format_date, hide_if, show_if, parse_args,
|
||||
query_param, abort, require_fresh_login)
|
||||
query_param, abort, require_fresh_login, path_param)
|
||||
|
||||
from endpoints.api.logs import get_logs
|
||||
|
||||
|
@ -166,6 +166,7 @@ class SuperUserSendRecoveryEmail(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/superuser/users/<username>')
|
||||
@path_param('username', 'The username of the user being managed')
|
||||
@internal_only
|
||||
@show_if(features.SUPER_USERS)
|
||||
class SuperUserManagement(ApiResource):
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
from flask import request
|
||||
|
||||
from endpoints.api import (resource, nickname, require_repo_read, require_repo_write,
|
||||
RepositoryParamResource, log_action, NotFound, validate_json_request)
|
||||
RepositoryParamResource, log_action, NotFound, validate_json_request,
|
||||
path_param)
|
||||
from endpoints.api.image import image_view
|
||||
from data import model
|
||||
from auth.auth_context import get_authenticated_user
|
||||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/tag/<tag>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('tag', 'The name of the tag')
|
||||
class RepositoryTag(RepositoryParamResource):
|
||||
""" Resource for managing repository tags. """
|
||||
schemas = {
|
||||
|
@ -73,6 +76,8 @@ class RepositoryTag(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/tag/<tag>/images')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('tag', 'The name of the tag')
|
||||
class RepositoryTagImages(RepositoryParamResource):
|
||||
""" Resource for listing the images in a specific repository tag. """
|
||||
@require_repo_read
|
||||
|
|
|
@ -2,13 +2,14 @@ from flask import request
|
|||
|
||||
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
|
||||
log_action, Unauthorized, NotFound, internal_only, require_scope,
|
||||
query_param, truthy_bool, parse_args, require_user_admin, show_if)
|
||||
path_param, query_param, truthy_bool, parse_args, require_user_admin,
|
||||
show_if)
|
||||
from auth.permissions import AdministerOrganizationPermission, ViewTeamPermission
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from data import model
|
||||
from util.useremails import send_org_invite_email
|
||||
from util.gravatar import compute_hash
|
||||
from app import avatar
|
||||
|
||||
import features
|
||||
|
||||
|
@ -63,7 +64,7 @@ def member_view(member, invited=False):
|
|||
'name': member.username,
|
||||
'kind': 'user',
|
||||
'is_robot': member.robot,
|
||||
'gravatar': compute_hash(member.email) if not member.robot else None,
|
||||
'avatar': avatar.compute_hash(member.email, name=member.username) if not member.robot else None,
|
||||
'invited': invited,
|
||||
}
|
||||
|
||||
|
@ -75,13 +76,14 @@ def invite_view(invite):
|
|||
return {
|
||||
'email': invite.email,
|
||||
'kind': 'invite',
|
||||
'gravatar': compute_hash(invite.email),
|
||||
'avatar': avatar.compute_hash(invite.email),
|
||||
'invited': True
|
||||
}
|
||||
|
||||
|
||||
@resource('/v1/organization/<orgname>/team/<teamname>')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('teamname', 'The name of the team')
|
||||
class OrganizationTeam(ApiResource):
|
||||
""" Resource for manging an organization's teams. """
|
||||
schemas = {
|
||||
|
@ -110,6 +112,7 @@ class OrganizationTeam(ApiResource):
|
|||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('updateOrganizationTeam')
|
||||
@validate_json_request('TeamDescription')
|
||||
def put(self, orgname, teamname):
|
||||
|
@ -151,6 +154,7 @@ class OrganizationTeam(ApiResource):
|
|||
|
||||
raise Unauthorized()
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('deleteOrganizationTeam')
|
||||
def delete(self, orgname, teamname):
|
||||
""" Delete the specified team. """
|
||||
|
@ -164,9 +168,11 @@ class OrganizationTeam(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/team/<teamname>/members')
|
||||
@internal_only
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('teamname', 'The name of the team')
|
||||
class TeamMemberList(ApiResource):
|
||||
""" Resource for managing the list of members for a team. """
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@parse_args
|
||||
@query_param('includePending', 'Whether to include pending members', type=truthy_bool, default=False)
|
||||
@nickname('getOrganizationTeamMembers')
|
||||
|
@ -199,8 +205,12 @@ class TeamMemberList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/team/<teamname>/members/<membername>')
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('teamname', 'The name of the team')
|
||||
@path_param('membername', 'The username of the team member')
|
||||
class TeamMember(ApiResource):
|
||||
""" Resource for managing individual members of a team. """
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('updateOrganizationTeamMember')
|
||||
def put(self, orgname, teamname, membername):
|
||||
|
|
|
@ -8,7 +8,8 @@ from urlparse import urlunparse
|
|||
from app import app
|
||||
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
||||
log_action, request_error, query_param, parse_args, internal_only,
|
||||
validate_json_request, api, Unauthorized, NotFound, InvalidRequest)
|
||||
validate_json_request, api, Unauthorized, NotFound, InvalidRequest,
|
||||
path_param)
|
||||
from endpoints.api.build import (build_status_view, trigger_view, RepositoryBuildStatus,
|
||||
get_trigger_config)
|
||||
from endpoints.common import start_build
|
||||
|
@ -30,6 +31,7 @@ def _prepare_webhook_url(scheme, username, password, hostname, path):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class BuildTriggerList(RepositoryParamResource):
|
||||
""" Resource for listing repository build triggers. """
|
||||
|
||||
|
@ -44,6 +46,8 @@ class BuildTriggerList(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
class BuildTrigger(RepositoryParamResource):
|
||||
""" Resource for managing specific build triggers. """
|
||||
|
||||
|
@ -90,6 +94,8 @@ class BuildTrigger(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/subdir')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
@internal_only
|
||||
class BuildTriggerSubdirs(RepositoryParamResource):
|
||||
""" Custom verb for fetching the subdirs which are buildable for a trigger. """
|
||||
|
@ -137,7 +143,8 @@ class BuildTriggerSubdirs(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/activate')
|
||||
@internal_only
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
class BuildTriggerActivate(RepositoryParamResource):
|
||||
""" Custom verb for activating a build trigger once all required information has been collected.
|
||||
"""
|
||||
|
@ -234,6 +241,8 @@ class BuildTriggerActivate(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/analyze')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
@internal_only
|
||||
class BuildTriggerAnalyze(RepositoryParamResource):
|
||||
""" Custom verb for analyzing the config for a build trigger and suggesting various changes
|
||||
|
@ -369,6 +378,8 @@ class BuildTriggerAnalyze(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/start')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
class ActivateBuildTrigger(RepositoryParamResource):
|
||||
""" Custom verb to manually activate a build trigger. """
|
||||
schemas = {
|
||||
|
@ -424,6 +435,8 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/builds')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
class TriggerBuildList(RepositoryParamResource):
|
||||
""" Resource to represent builds that were activated from the specified trigger. """
|
||||
@require_repo_admin
|
||||
|
@ -471,6 +484,8 @@ class BuildTriggerFieldValues(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/sources')
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
@path_param('trigger_uuid', 'The UUID of the build trigger')
|
||||
@internal_only
|
||||
class BuildTriggerSources(RepositoryParamResource):
|
||||
""" Custom verb to fetch the list of build sources for the trigger config. """
|
||||
|
|
|
@ -5,11 +5,11 @@ from flask import request
|
|||
from flask.ext.login import logout_user
|
||||
from flask.ext.principal import identity_changed, AnonymousIdentity
|
||||
|
||||
from app import app, billing as stripe, authentication
|
||||
from app import app, billing as stripe, authentication, avatar
|
||||
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
|
||||
log_action, internal_only, NotFound, require_user_admin, parse_args,
|
||||
query_param, InvalidToken, require_scope, format_date, hide_if, show_if,
|
||||
license_error, require_fresh_login)
|
||||
license_error, require_fresh_login, path_param, define_json_response)
|
||||
from endpoints.api.subscribe import subscribe
|
||||
from endpoints.common import common_login
|
||||
from endpoints.api.team import try_accept_invite
|
||||
|
@ -20,7 +20,6 @@ from auth.permissions import (AdministerOrganizationPermission, CreateRepository
|
|||
UserAdminPermission, UserReadPermission, SuperUserPermission)
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from util.gravatar import compute_hash
|
||||
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email, send_password_changed)
|
||||
from util.names import parse_single_urn
|
||||
|
||||
|
@ -34,7 +33,7 @@ def user_view(user):
|
|||
admin_org = AdministerOrganizationPermission(o.username)
|
||||
return {
|
||||
'name': o.username,
|
||||
'gravatar': compute_hash(o.email),
|
||||
'avatar': avatar.compute_hash(o.email, name=o.username),
|
||||
'is_org_admin': admin_org.can(),
|
||||
'can_create_repo': admin_org.can() or CreateRepositoryPermission(o.username).can(),
|
||||
'preferred_namespace': not (o.stripe_id is None)
|
||||
|
@ -60,13 +59,13 @@ def user_view(user):
|
|||
'verified': user.verified,
|
||||
'anonymous': False,
|
||||
'username': user.username,
|
||||
'email': user.email,
|
||||
'gravatar': compute_hash(user.email),
|
||||
'avatar': avatar.compute_hash(user.email, name=user.username),
|
||||
}
|
||||
|
||||
user_admin = UserAdminPermission(user.username)
|
||||
if user_admin.can():
|
||||
user_response.update({
|
||||
'email': user.email,
|
||||
'organizations': [org_view(o) for o in organizations],
|
||||
'logins': [login_view(login) for login in logins],
|
||||
'can_create_repo': True,
|
||||
|
@ -149,10 +148,51 @@ class User(ApiResource):
|
|||
},
|
||||
},
|
||||
},
|
||||
'UserView': {
|
||||
'id': 'UserView',
|
||||
'type': 'object',
|
||||
'description': 'Describes a user',
|
||||
'required': ['verified', 'anonymous', 'avatar'],
|
||||
'properties': {
|
||||
'verified': {
|
||||
'type': 'boolean',
|
||||
'description': 'Whether the user\'s email address has been verified'
|
||||
},
|
||||
'anonymous': {
|
||||
'type': 'boolean',
|
||||
'description': 'true if this user data represents a guest user'
|
||||
},
|
||||
'email': {
|
||||
'type': 'string',
|
||||
'description': 'The user\'s email address',
|
||||
},
|
||||
'avatar': {
|
||||
'type': 'string',
|
||||
'description': 'Avatar hash representing the user\'s icon'
|
||||
},
|
||||
'organizations': {
|
||||
'type': 'array',
|
||||
'description': 'Information about the organizations in which the user is a member'
|
||||
},
|
||||
'logins': {
|
||||
'type': 'array',
|
||||
'description': 'The list of external login providers against which the user has authenticated'
|
||||
},
|
||||
'can_create_repo': {
|
||||
'type': 'boolean',
|
||||
'description': 'Whether the user has permission to create repositories'
|
||||
},
|
||||
'preferred_namespace': {
|
||||
'type': 'boolean',
|
||||
'description': 'If true, the user\'s namespace is the preferred namespace to display'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@require_scope(scopes.READ_USER)
|
||||
@nickname('getLoggedInUser')
|
||||
@define_json_response('UserView')
|
||||
def get(self):
|
||||
""" Get user information for the authenticated user. """
|
||||
user = get_authenticated_user()
|
||||
|
@ -166,6 +206,7 @@ class User(ApiResource):
|
|||
@nickname('changeUserDetails')
|
||||
@internal_only
|
||||
@validate_json_request('UpdateUser')
|
||||
@define_json_response('UserView')
|
||||
def put(self):
|
||||
""" Update a users details such as password or email. """
|
||||
user = get_authenticated_user()
|
||||
|
@ -526,6 +567,7 @@ class UserNotificationList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/user/notifications/<uuid>')
|
||||
@path_param('uuid', 'The uuid of the user notification')
|
||||
@internal_only
|
||||
class UserNotification(ApiResource):
|
||||
schemas = {
|
||||
|
@ -572,10 +614,12 @@ def authorization_view(access_token):
|
|||
'name': oauth_app.name,
|
||||
'description': oauth_app.description,
|
||||
'url': oauth_app.application_uri,
|
||||
'gravatar': compute_hash(oauth_app.gravatar_email or oauth_app.organization.email),
|
||||
'avatar': avatar.compute_hash(oauth_app.avatar_email or oauth_app.organization.email,
|
||||
name=oauth_app.name),
|
||||
'organization': {
|
||||
'name': oauth_app.organization.username,
|
||||
'gravatar': compute_hash(oauth_app.organization.email)
|
||||
'avatar': avatar.compute_hash(oauth_app.organization.email,
|
||||
name=oauth_app.organization.username)
|
||||
}
|
||||
},
|
||||
'scopes': scopes.get_scope_information(access_token.scope),
|
||||
|
@ -596,6 +640,7 @@ class UserAuthorizationList(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/user/authorizations/<access_token_uuid>')
|
||||
@path_param('access_token_uuid', 'The uuid of the access token')
|
||||
@internal_only
|
||||
class UserAuthorization(ApiResource):
|
||||
@require_user_admin
|
||||
|
|
Reference in a new issue