diff --git a/.gitignore b/.gitignore index 0bc08bcca..e1b88b2cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ __pycache__/ *.pyc +.coverage .idea/ .eggs/ .pytest_cache/ build/ +coverage/ dist/ docs/_build/ docs/magpie.esgf.rst @@ -17,6 +19,7 @@ env/ magpieenv/ magpie/api_docs/ magpie/api_test/ +magpie/ui/swagger-ui/magpie-rest-api.json magpie/bin/ magpie/include/ magpie/lib/ diff --git a/Dockerfile b/Dockerfile index 35ffe91c4..c852ed34a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM ubuntu:16.04 -MAINTAINER Francois-Xavier Derue +MAINTAINER Francis Charette-Migneault RUN apt-get update && apt-get install -y \ build-essential \ diff --git a/Makefile b/Makefile index e845bc435..e332e1ceb 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ clean-pyc: clean-test: rm -fr .tox/ rm -f .coverage - rm -fr htmlcov/ + rm -fr coverage/ lint: flake8 magpie tests @@ -59,8 +59,8 @@ test-all: coverage: coverage run --source magpie setup.py test coverage report -m - coverage html - $(BROWSER) htmlcov/index.html + coverage html -d coverage + $(BROWSER) coverage/index.html migrate: alembic upgrade head diff --git a/README.rst b/README.rst index 0de8b0dd9..f19605eac 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,12 @@ Magpie (the smart-bird) Magpie is service for AuthN/AuthZ accessible via a `RestAPI`_ implemented with the Pyramid web framework. It allows you to manage User/Group/Resource/permission with a postgres database. Behind the scene, it uses `Ziggurat-Foundations`_ and `Authomatic`_. +REST API Documentation +====================== + +The documentation is auto-generated and served under `{HOSTNAME}/magpie/api/` using Swagger-UI with tag `latest`. +For convenience, older API versions are also provided. + Build package ============= diff --git a/bin/gunicorn b/bin/gunicorn index 1c356aff1..64b69ed5f 100755 --- a/bin/gunicorn +++ b/bin/gunicorn @@ -1,6 +1,5 @@ import sys import gunicorn.app.wsgiapp -import sys if __name__ == '__main__': diff --git a/docs/sync.py b/docs/sync.py index c5ff7c2dc..4f7810f71 100644 --- a/docs/sync.py +++ b/docs/sync.py @@ -5,7 +5,7 @@ """ from subprocess import check_call -from urllib.parse import urljoin +from six.moves.urllib.parse import urljoin from shlex import split import argparse diff --git a/magpie/__init__.py b/magpie/__init__.py index f22563490..d70bed5b3 100644 --- a/magpie/__init__.py +++ b/magpie/__init__.py @@ -2,8 +2,12 @@ import os import sys -this_dir = os.path.abspath(os.path.dirname(__file__)) -sys.path.insert(0, this_dir) +MAGPIE_MODULE_DIR = os.path.abspath(os.path.dirname(__file__)) +MAGPIE_ROOT = os.path.dirname(MAGPIE_MODULE_DIR) +sys.path.insert(0, MAGPIE_MODULE_DIR) + +MAGPIE_PROVIDERS_CONFIG_PATH = '{}/providers.cfg'.format(MAGPIE_ROOT) +MAGPIE_INI_FILE_PATH = '{}/magpie.ini'.format(MAGPIE_MODULE_DIR) ADMIN_USER = os.getenv('ADMIN_USER', 'admin') ADMIN_GROUP = os.getenv('ADMIN_GROUP', 'administrators') @@ -12,6 +16,8 @@ USER_GROUP = os.getenv('USER_GROUP', 'users') ANONYMOUS_USER = os.getenv('ANONYMOUS_USER', 'anonymous') +ANONYMOUS_GROUP = ANONYMOUS_USER +ANONYMOUS_PASSWORD = ANONYMOUS_USER ADMIN_PERM = 'admin' #ADMIN_PERM = NO_PERMISSION_REQUIRED @@ -29,7 +35,7 @@ def includeme(config): config.include('cornice_swagger') config.include('pyramid_chameleon') config.include('pyramid_mako') - config.include('definitions') - config.include('api') - config.include('db') - config.include('ui') + config.include('magpie.definitions') + config.include('magpie.api') + config.include('magpie.db') + config.include('magpie.ui') diff --git a/magpie/__meta__.py b/magpie/__meta__.py index cca2bac7b..27eba4487 100644 --- a/magpie/__meta__.py +++ b/magpie/__meta__.py @@ -2,7 +2,9 @@ General meta information on the magpie package. """ - -__version__ = '0.6.2' +__version__ = '0.6.3' __author__ = "Francois-Xavier Derue, Francis Charette-Migneault" +__maintainer__ = "Francis Charette-Migneault" __email__ = 'francis.charette-migneault@crim.ca' +__url__ = 'https://github.com/Ouranosinc/Magpie' +__description__ = "Magpie is a service for AuthN and AuthZ based on Ziggurat-Foundations" diff --git a/magpie/adapter/__init__.py b/magpie/adapter/__init__.py index f3f1ebf30..6afdd780d 100644 --- a/magpie/adapter/__init__.py +++ b/magpie/adapter/__init__.py @@ -9,10 +9,9 @@ import logging logger = logging.getLogger(__name__) - class MagpieAdapter(AdapterInterface): - def servicestore_factory(self, registry, database=None): + def servicestore_factory(self, registry, database=None, headers=None): return MagpieServiceStore(registry=registry) def owssecurity_factory(self, registry): diff --git a/magpie/alembic/versions/20671b28c538_change_all_linking_k.py b/magpie/alembic/versions/20671b28c538_change_all_linking_k.py index 17114fd7c..2f08716c3 100644 --- a/magpie/alembic/versions/20671b28c538_change_all_linking_k.py +++ b/magpie/alembic/versions/20671b28c538_change_all_linking_k.py @@ -13,7 +13,7 @@ from alembic import op from alembic.context import get_context -from definitions.sqlalchemy_definitions import * +from magpie.definitions.sqlalchemy_definitions import * def upgrade(): diff --git a/magpie/alembic/versions/5e7b5346c330_remove_obsolete_personal_groups.py b/magpie/alembic/versions/5e7b5346c330_remove_obsolete_personal_groups.py index 500c215ff..2b2e67d14 100644 --- a/magpie/alembic/versions/5e7b5346c330_remove_obsolete_personal_groups.py +++ b/magpie/alembic/versions/5e7b5346c330_remove_obsolete_personal_groups.py @@ -19,7 +19,7 @@ from sqlalchemy.dialects.postgresql.base import PGDialect from sqlalchemy.orm import sessionmaker from magpie import models, ANONYMOUS_USER, ADMIN_GROUP, USER_GROUP -from definitions.ziggurat_definitions import * +from magpie.definitions.ziggurat_definitions import * Session = sessionmaker() diff --git a/magpie/alembic/versions/a395ef9d3fe6_reference_root_service.py b/magpie/alembic/versions/a395ef9d3fe6_reference_root_service.py index 26ce0645b..00b11a2ca 100644 --- a/magpie/alembic/versions/a395ef9d3fe6_reference_root_service.py +++ b/magpie/alembic/versions/a395ef9d3fe6_reference_root_service.py @@ -15,7 +15,7 @@ from alembic import op from alembic.context import get_context -from definitions.sqlalchemy_definitions import * +from magpie.definitions.sqlalchemy_definitions import * from magpie import models from magpie.api.management.resource.resource_utils import get_resource_root_service diff --git a/magpie/alembic/versions/ae1a3c8c7860_tranfer_group_users_admins_users.py b/magpie/alembic/versions/ae1a3c8c7860_tranfer_group_users_admins_users.py index 6eb621faa..78b2f2e76 100644 --- a/magpie/alembic/versions/ae1a3c8c7860_tranfer_group_users_admins_users.py +++ b/magpie/alembic/versions/ae1a3c8c7860_tranfer_group_users_admins_users.py @@ -15,9 +15,9 @@ from alembic import op from alembic.context import get_context -from definitions.sqlalchemy_definitions import * +from magpie.definitions.sqlalchemy_definitions import * from magpie import models, ANONYMOUS_USER -from definitions.ziggurat_definitions import * +from magpie.definitions.ziggurat_definitions import * Session = sessionmaker() diff --git a/magpie/api/__init__.py b/magpie/api/__init__.py index 4d7f5e2e3..e43fe8427 100644 --- a/magpie/api/__init__.py +++ b/magpie/api/__init__.py @@ -6,10 +6,10 @@ def includeme(config): logger.info('Adding api routes ...') # Add all the admin ui routes - config.include('api.esgf') - config.include('api.home') - config.include('api.login') - config.include('api.management') + config.include('magpie.api.esgf') + config.include('magpie.api.home') + config.include('magpie.api.login') + config.include('magpie.api.management') config.add_route('version', '/version') config.scan() diff --git a/magpie/api/api_except.py b/magpie/api/api_except.py index 044de2664..76507773a 100644 --- a/magpie/api/api_except.py +++ b/magpie/api/api_except.py @@ -1,6 +1,7 @@ from pyramid.httpexceptions import * from sys import exc_info import types +import six # control variables to avoid infinite recursion in case of # major programming error to avoid application hanging @@ -11,7 +12,8 @@ def verify_param(param, paramCompare=None, httpError=HTTPNotAcceptable, httpKWArgs=None, msgOnFail="", content=None, contentType='application/json', notNone=False, notEmpty=False, notIn=False, notEqual=False, - isNone=False, isEmpty=False, isIn=False, isEqual=False, ofType=None, withParam=True): + isNone=False, isEmpty=False, isIn=False, isEqual=False, ofType=None, + withParam=True, paramName=None): """ Evaluate various parameter combinations given the requested flags. Given a failing verification, directly raises the specified `httpError`. @@ -19,6 +21,7 @@ def verify_param(param, paramCompare=None, httpError=HTTPNotAcceptable, httpKWAr Exceptions are generated using the standard output method. :param param: (bool) parameter value to evaluate + :param paramName: (str) name of the tested parameter returned in response if specified for debugging purposes :param paramCompare: other value(s) to test against, can be an iterable (single value resolved as iterable unless None) to test for None type, use `isNone`/`notNone` flags instead or `paramCompare`=[None] @@ -96,9 +99,11 @@ def verify_param(param, paramCompare=None, httpError=HTTPNotAcceptable, httpKWAr status = status or (not type(param) == ofType) if status: if withParam: - content[u'param'] = repr(param) + content[u'param'] = {u'value': str(param) if type(param) in six.string_types else repr(param)} + if paramName is not None: + content[u'param'][u'name'] = str(paramName) if paramCompare is not None: - content[u'paramCompare'] = repr(paramCompare) + content[u'param'][u'compare'] = repr(paramCompare) raise_http(httpError, httpKWArgs=httpKWArgs, detail=msgOnFail, content=content, contentType=contentType) @@ -329,7 +334,7 @@ def generate_response_http_format(httpClass, httpKWArgs, jsonContent, outputType try: # directly output json if asked with 'application/json' if outputType == 'application/json': - httpResponse = httpClass(body=jsonContent, content_type='application/json', **httpKWArgs) + httpResponse = httpClass(body=jsonContent, content_type='application/json; charset=UTF-8', **httpKWArgs) # otherwise json is contained within the html
section elif outputType == 'text/html': @@ -363,4 +368,4 @@ def isclass(obj): :param obj: object to evaluate for class type :return: (bool) indicating if `object` is a class """ - return isinstance(obj, (type, types.ClassType)) + return isinstance(obj, (type, six.class_types)) diff --git a/magpie/api/api_generic.py b/magpie/api/api_generic.py new file mode 100644 index 000000000..24c1b61a3 --- /dev/null +++ b/magpie/api/api_generic.py @@ -0,0 +1,53 @@ +from magpie.definitions.pyramid_definitions import * +from magpie.api.api_except import * +from magpie.api.api_rest_schemas import * +from magpie import __meta__, db + + +@VersionAPI.get(tags=[APITag], api_security=SecurityEveryoneAPI, response_schemas=Version_GET_responses) +@view_config(route_name='version', request_method='GET', permission=NO_PERMISSION_REQUIRED) +def get_version(request): + """ + Version information of the API. + """ + return valid_http(httpSuccess=HTTPOk, + content={u'version': __meta__.__version__, u'db_version': db.get_database_revision(request.db)}, + detail=Version_GET_OkResponseSchema.description, contentType='application/json') + + +@notfound_view_config() +def not_found(request): + content = get_request_info(request, default_msg=NotFoundResponseSchema.description) + return raise_http(nothrow=True, httpError=HTTPNotFound, contentType='application/json', + detail=content['detail'], content=content) + + +@exception_view_config() +def internal_server_error(request): + content = get_request_info(request, default_msg=InternalServerErrorResponseSchema.description) + return raise_http(nothrow=True, httpError=HTTPInternalServerError, contentType='application/json', + detail=content['detail'], content=content) + + +@forbidden_view_config() +def unauthorized_access(request): + # if not overridden, default is HTTPForbidden [403], which is for a slightly different situation + # this better reflects the HTTPUnauthorized [401] user access with specified AuthZ headers + # [http://www.restapitutorial.com/httpstatuscodes.html] + content = get_request_info(request, default_msg=UnauthorizedResponseSchema.description) + return raise_http(nothrow=True, httpError=HTTPUnauthorized, contentType='application/json', + detail=content['detail'], content=content) + + +def get_request_info(request, default_msg="undefined"): + content = {u'route_name': str(request.upath_info), u'request_url': str(request.url), u'detail': default_msg} + if hasattr(request, 'exception'): + if hasattr(request.exception, 'json'): + if type(request.exception.json) is dict: + content.update(request.exception.json) + elif isinstance(request.exception, HTTPServerError) and hasattr(request.exception, 'message'): + content.update({u'exception': str(request.exception.message)}) + elif hasattr(request, 'matchdict'): + if request.matchdict is not None and request.matchdict != '': + content.update(request.matchdict) + return content diff --git a/magpie/api/api_requests.py b/magpie/api/api_requests.py index bea74da38..63347dc3c 100644 --- a/magpie/api/api_requests.py +++ b/magpie/api/api_requests.py @@ -1,8 +1,8 @@ from magpie import * -from definitions.pyramid_definitions import * -from definitions.ziggurat_definitions import * -from api.api_except import evaluate_call, verify_param -from api.management.resource.resource_utils import check_valid_service_resource_permission +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.ziggurat_definitions import * +from magpie.api.api_except import evaluate_call, verify_param +from magpie.api.api_rest_schemas import * import models @@ -12,29 +12,34 @@ def get_request_method_content(request): return getattr(request, method_property) -def get_multiformat_any(request, key): +def get_multiformat_any(request, key, default=None): msg = "Key `{key}` could not be extracted from {method} of type `{type}`" \ .format(key=repr(key), method=request.method, type=request.content_type) if request.content_type == 'application/json': - return evaluate_call(lambda: request.json.get(key), + # avoid json parse error if body is empty + if not len(request.body): + return default + return evaluate_call(lambda: request.json.get(key, default), httpError=HTTPInternalServerError, msgOnFail=msg) - return evaluate_call(lambda: get_request_method_content(request).get(key), + return evaluate_call(lambda: get_request_method_content(request).get(key, default), httpError=HTTPInternalServerError, msgOnFail=msg) -def get_multiformat_post(request, key): - return get_multiformat_any(request, key) +def get_multiformat_post(request, key, default=None): + return get_multiformat_any(request, key, default) -def get_multiformat_put(request, key): - return get_multiformat_any(request, key) +def get_multiformat_put(request, key, default=None): + return get_multiformat_any(request, key, default) -def get_multiformat_delete(request, key): - return get_multiformat_any(request, key) +def get_multiformat_delete(request, key, default=None): + return get_multiformat_any(request, key, default) def get_permission_multiformat_post_checked(request, service_resource, permission_name_key='permission_name'): + # import here to avoid circular import error with undefined functions between (api_request, resource_utils) + from magpie.api.management.resource.resource_utils import check_valid_service_resource_permission perm_name = get_value_multiformat_post_checked(request, permission_name_key) check_valid_service_resource_permission(perm_name, service_resource, request.db) return perm_name @@ -43,7 +48,7 @@ def get_permission_multiformat_post_checked(request, service_resource, permissio def get_value_multiformat_post_checked(request, key): val = get_multiformat_any(request, key) verify_param(val, notNone=True, notEmpty=True, httpError=HTTPUnprocessableEntity, - content={str(key): str(val)}, msgOnFail="Invalid value specified.") + paramName=key, msgOnFail=UnprocessableEntityResponseSchema.description) return val @@ -70,9 +75,10 @@ def get_user(request, user_name_or_token): return curr_user else: anonymous = evaluate_call(lambda: UserService.by_user_name(ANONYMOUS_USER, db_session=request.db), - fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="Anonymous user query refused by db") - verify_param(anonymous, notNone=True, httpError=HTTPNotFound, msgOnFail="Anonymous user not found in db") + fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, + msgOnFail=User_CheckAnonymous_ForbiddenResponseSchema.description) + verify_param(anonymous, notNone=True, httpError=HTTPNotFound, + msgOnFail=User_CheckAnonymous_NotFoundResponseSchema.description) return anonymous else: authn_policy = request.registry.queryUtility(IAuthenticationPolicy) @@ -83,8 +89,8 @@ def get_user(request, user_name_or_token): raise HTTPForbidden() user = evaluate_call(lambda: UserService.by_user_name(user_name_or_token, db_session=request.db), fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="User name query refused by db") - verify_param(user, notNone=True, httpError=HTTPNotFound, msgOnFail="User name not found in db") + httpError=HTTPForbidden, msgOnFail=User_GET_ForbiddenResponseSchema.description) + verify_param(user, notNone=True, httpError=HTTPNotFound, msgOnFail=User_GET_NotFoundResponseSchema.description) return user @@ -97,34 +103,37 @@ def get_group_matchdict_checked(request, group_name_key='group_name'): group_name = get_value_matchdict_checked(request, group_name_key) group = evaluate_call(lambda: GroupService.by_group_name(group_name, db_session=request.db), fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="Group query by name refused by db") - verify_param(group, notNone=True, httpError=HTTPNotFound, msgOnFail="Group name not found in db") + httpError=HTTPForbidden, msgOnFail=Group_MatchDictCheck_ForbiddenResponseSchema.description) + verify_param(group, notNone=True, httpError=HTTPNotFound, + msgOnFail=Group_MatchDictCheck_NotFoundResponseSchema.description) return group def get_resource_matchdict_checked(request, resource_name_key='resource_id'): resource_id = get_value_matchdict_checked(request, resource_name_key) resource_id = evaluate_call(lambda: int(resource_id), httpError=HTTPNotAcceptable, - msgOnFail="Resource ID is an invalid literal for `int` type") + msgOnFail=Resource_MatchDictCheck_NotAcceptableResponseSchema.description) resource = evaluate_call(lambda: ResourceService.by_resource_id(resource_id, db_session=request.db), - fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="Resource query by id refused by db") - verify_param(resource, notNone=True, httpError=HTTPNotFound, msgOnFail="Resource ID not found in db") + fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, + msgOnFail=Resource_MatchDictCheck_ForbiddenResponseSchema.description) + verify_param(resource, notNone=True, httpError=HTTPNotFound, + msgOnFail=Resource_MatchDictCheck_NotFoundResponseSchema.description) return resource def get_service_matchdict_checked(request, service_name_key='service_name'): service_name = get_value_matchdict_checked(request, service_name_key) service = evaluate_call(lambda: models.Service.by_service_name(service_name, db_session=request.db), - fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="Service query by name refused by db") - verify_param(service, notNone=True, httpError=HTTPNotFound, - msgOnFail="Service name not found in db", - content={u'service_name': service_name}) + fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, + msgOnFail=Service_MatchDictCheck_ForbiddenResponseSchema.description) + verify_param(service, notNone=True, httpError=HTTPNotFound, content={u'service_name': service_name}, + msgOnFail=Service_MatchDictCheck_NotFoundResponseSchema.description) return service def get_permission_matchdict_checked(request, service_resource, permission_name_key='permission_name'): + # import here to avoid circular import error with undefined functions between (api_request, resource_utils) + from magpie.api.management.resource.resource_utils import check_valid_service_resource_permission perm_name = get_value_matchdict_checked(request, permission_name_key) check_valid_service_resource_permission(perm_name, service_resource, request.db) return perm_name @@ -133,5 +142,5 @@ def get_permission_matchdict_checked(request, service_resource, permission_name_ def get_value_matchdict_checked(request, key): val = request.matchdict.get(key) verify_param(val, notNone=True, notEmpty=True, httpError=HTTPUnprocessableEntity, - content={str(key): str(val)}, msgOnFail="Invalid value specified") + paramName=key, msgOnFail=UnprocessableEntityResponseSchema.description) return val diff --git a/magpie/api/api_rest_schemas.py b/magpie/api/api_rest_schemas.py index 63ee005ba..6eea55523 100644 --- a/magpie/api/api_rest_schemas.py +++ b/magpie/api/api_rest_schemas.py @@ -1,8 +1,6 @@ -from definitions.cornice_definitions import * -from definitions.pyramid_definitions import view_config -from db import get_database_revision -from __meta__ import __version__ -import requests +from magpie.definitions.cornice_definitions import * +from magpie.definitions.pyramid_definitions import * +from magpie import LOGGED_USER, __meta__ class CorniceSwaggerPredicate(object): @@ -18,27 +16,65 @@ def __call__(self, context, request): return self.schema +TitleAPI = "Magpie REST API" +InfoAPI = { + "description": __meta__.__description__, + "contact": {"name": __meta__.__maintainer__, "email": __meta__.__email__, "url": __meta__.__url__} +} + + # Tags APITag = 'API' -UserTag = 'User' -GroupTag = 'Group' -ResourceTag = 'Resource' -ServiceTag = 'Service' +LoginTag = 'Login' +UsersTag = 'User' +LoggedUserTag = 'Logged User' +GroupsTag = 'Group' +ResourcesTag = 'Resource' +ServicesTag = 'Service' + + +# Security +SecurityDefinitionAPI = {'securityDefinitions': {'cookieAuth': {'type': 'apiKey', 'in': 'cookie', 'name': 'auth_tkt'}}} +SecurityAdministratorAPI = [{'cookieAuth': []}] +SecurityEveryoneAPI = [] + + +def get_security(service, method): + definitions = service.definitions + args = {} + for definition in definitions: + met, view, args = definition + if met == method: + break + return SecurityAdministratorAPI if 'security' not in args else args['security'] # Service Routes +def service_api_route_info(service_api): + return {'name': service_api.name, 'pattern': service_api.path} + + +LoggedUserBase = '/users/{}'.format(LOGGED_USER) + + +SwaggerGenerator = Service( + path='/__api__', + name='swagger_schema') SwaggerAPI = Service( - path='__api__', - name='Magpie REST API', - description="Magpie REST API documentation") + path='/api', + name='swagger', + description="{} documentation".format(TitleAPI)) +UsersAPI = Service( + path='/users', + name='Users') UserAPI = Service( path='/users/{user_name}', name='User') -UsersAPI = Service( - path='/users/{user_name}', - name='Users') -UserGroupAPI = Service( +UserGroupsAPI = Service( path='/users/{user_name}/groups', + name='UserGroups') +UserGroupAPI = Service( + path='/users/{user_name}/groups/{group_name}', name='UserGroup') UserInheritedResourcesAPI = Service( path='/users/{user_name}/inherited_resources', @@ -46,52 +82,1425 @@ def __call__(self, context, request): UserResourcesAPI = Service( path='/users/{user_name}/resources', name='UserResources') -UserResourcesPermissionsAPI = Service( - path='/users/{user_name}/resources', - name='UserResourcesPermissions') +UserResourceInheritedPermissionsAPI = Service( + path='/users/{user_name}/resources/{resource_id}/inherited_permissions', + name='UserResourceInheritedPermissions') +UserResourcePermissionAPI = Service( + path='/users/{user_name}/resources/{resource_id}/permissions/{permission_name}', + name='UserResourcePermission') +UserResourcePermissionsAPI = Service( + path='/users/{user_name}/resources/{resource_id}/permissions', + name='UserResourcePermissions') +UserResourceTypesAPI = Service( + path='/users/{user_name}/resources/types/{resource_type}', + name='UserResourceTypes') UserInheritedServicesAPI = Service( path='/users/{user_name}/inherited_services', name='UserInheritedServices') UserServicesAPI = Service( path='/users/{user_name}/services', name='UserServices') -CurrentUserAPI = Service( - path='/users/current', - name='CurrentUser') +UserServiceAPI = Service( + path='/users/{user_name}/services/{service_name}', + name='UserService') +UserServiceInheritedResourcesAPI = Service( + path='/users/{user_name}/services/{service_name}/inherited_resources', + name='UserServiceInheritedResources') +UserServiceResourcesAPI = Service( + path='/users/{user_name}/services/{service_name}/resources', + name='UserServiceResources') +UserServiceInheritedPermissionsAPI = Service( + path='/users/{user_name}/services/{service_name}/inherited_permissions', + name='UserServiceInheritedPermissions') +UserServicePermissionsAPI = Service( + path='/users/{user_name}/services/{service_name}/permissions', + name='UserServicePermissions') +UserServicePermissionAPI = Service( + path='/users/{user_name}/services/{service_name}/permissions/{permission_name}', + name='UserServicePermission') +LoggedUserAPI = Service( + path=LoggedUserBase, + name='LoggedUser') +LoggedUserGroupsAPI = Service( + path=LoggedUserBase + '/groups', + name='LoggedUserGroups') +LoggedUserGroupAPI = Service( + path=LoggedUserBase + '/groups/{group_name}', + name='LoggedUserGroup') +LoggedUserInheritedResourcesAPI = Service( + path=LoggedUserBase + '/inherited_resources', + name='LoggedUserInheritedResources') +LoggedUserResourcesAPI = Service( + path=LoggedUserBase + '/resources', + name='LoggedUserResources') +LoggedUserResourceInheritedPermissionsAPI = Service( + path=LoggedUserBase + '/resources/{resource_id}/inherited_permissions', + name='LoggedUserResourceInheritedPermissions') +LoggedUserResourcePermissionAPI = Service( + path=LoggedUserBase + '/resources/{resource_id}/permissions/{permission_name}', + name='LoggedUserResourcePermission') +LoggedUserResourcePermissionsAPI = Service( + path=LoggedUserBase + '/resources/{resource_id}/permissions', + name='LoggedUserResourcePermissions') +LoggedUserResourceTypesAPI = Service( + path=LoggedUserBase + '/resources/types/{resource_type}', + name='LoggedUserResourceTypes') +LoggedUserInheritedServicesAPI = Service( + path=LoggedUserBase + '/inherited_services', + name='LoggedUserInheritedServices') +LoggedUserServicesAPI = Service( + path=LoggedUserBase + '/services', + name='LoggedUserServices') +LoggedUserServiceInheritedResourcesAPI = Service( + path=LoggedUserBase + '/services/{service_name}/inherited_resources', + name='LoggedUserServiceInheritedResources') +LoggedUserServiceResourcesAPI = Service( + path=LoggedUserBase + '/services/{service_name}/resources', + name='LoggedUserServiceResources') +LoggedUserServiceInheritedPermissionsAPI = Service( + path=LoggedUserBase + '/services/{service_name}/inherited_permissions', + name='LoggedUserServiceInheritedPermissions') +LoggedUserServicePermissionsAPI = Service( + path=LoggedUserBase + '/services/{service_name}/permissions', + name='LoggedUserServicePermissions') +LoggedUserServicePermissionAPI = Service( + path=LoggedUserBase + '/services/{service_name}/permissions/{permission_name}', + name='LoggedUserServicePermission') +GroupsAPI = Service( + path='/groups', + name='Groups') GroupAPI = Service( path='/groups/{group_name}', name='Group') +GroupUsersAPI = Service( + path='/groups/{group_name}/users', + name='GroupUsers') +GroupServicesAPI = Service( + path='/groups/{group_name}/services', + name='GroupServices') +GroupServicePermissionsAPI = Service( + path='/groups/{group_name}/services/{service_name}/permissions', + name='GroupServicePermissions') +GroupServicePermissionAPI = Service( + path='/groups/{group_name}/services/{service_name}/permissions/{permission_name}', + name='GroupServicePermission') +GroupServiceResourcesAPI = Service( + path='/groups/{group_name}/services/{service_name}/resources', + name='GroupServiceResources') +GroupResourcesAPI = Service( + path='/groups/{group_name}/resources', + name='GroupResources') +GroupResourcePermissionsAPI = Service( + path='/groups/{group_name}/resources/{resource_id}/permissions', + name='GroupResourcePermissions') +GroupResourcePermissionAPI = Service( + path='/groups/{group_name}/resources/{resource_id}/permissions/{permission_name}', + name='GroupResourcePermission') +GroupResourceTypesAPI = Service( + path='/groups/{group_name}/resources/types/{resource_type}', + name='GroupResourceTypes') +ResourcesAPI = Service( + path='/resources', + name='Resources') ResourceAPI = Service( path='/resources/{resource_id}', name='Resource') +ResourcePermissionsAPI = Service( + path='/resources/{resource_id}/permissions', + name='ResourcePermissions') +ServicesAPI = Service( + path='/services', + name='Services') ServiceAPI = Service( path='/services/{service_name}', name='Service') +ServiceTypesAPI = Service( + path='/services/types/{service_type}', + name='ServiceTypes') +ServicePermissionsAPI = Service( + path='/services/{service_name}/permissions', + name='ServicePermissions') +ServiceResourcesAPI = Service( + path='/services/{service_name}/resources', + name='ServiceResources') +ServiceResourceAPI = Service( + path='/services/{service_name}/resources/{resource_id}', + name='ServiceResource') +ServiceResourceTypesAPI = Service( + path='/services/types/{service_type}/resources/types', + name='ServiceResourceTypes') +SessionAPI = Service( + path='/session', + name='Session') VersionAPI = Service( path='/version', name='Version') -#NotFoundAPI = Service(name='NotFound', path='/', description="Route not found") -class BaseSchema(colander.MappingSchema): - code = colander.SchemaNode(colander.Integer(), description="HTTP response code") - type = colander.SchemaNode(colander.String(), description="Response content type") - detail = colander.SchemaNode(colander.String(), description="Response status message") +CodeSchemaNode = colander.SchemaNode(colander.Integer(), description="HTTP response code", example=HTTPOk.code) +TypeSchemaNode = colander.SchemaNode(colander.String(), description="Response content type", example="application/json") +DetailSchemaNode = colander.SchemaNode(colander.String(), description="Response status message") + + +class HeaderSchema(colander.MappingSchema): + content_type = colander.SchemaNode( + colander.String(), + example='application/json' + ) + content_type.name = 'Content-Type' + + +class BaseBodySchema(colander.MappingSchema): + def __init__(self, code=None): + super(BaseBodySchema, self).__init__() + self.code = CodeSchemaNode + self.code.example = code + self.type = TypeSchemaNode + self.detail = DetailSchemaNode + + +class ErrorVerifyParamBodySchema(colander.MappingSchema): + name = colander.SchemaNode( + colander.String(), + description="Name of the failing condition parameter", + missing=colander.drop) + value = colander.SchemaNode( + colander.String(), + description="Value of the failing condition parameter") + compare = colander.SchemaNode( + colander.String(), + description="Test comparison value of the failing condition parameter", + missing=colander.drop) + + +class UnauthorizedResponseSchema(colander.MappingSchema): + description = "Unauthorized. Insufficient user privileges or missing authentication headers." + code = CodeSchemaNode + code.example = 401 + type = TypeSchemaNode + detail = DetailSchemaNode + route_name = colander.SchemaNode(colander.String(), description="Specified route") + request_url = colander.SchemaNode(colander.String(), description="Specified url") -#class NotFoundResponseSchema(colander.MappingSchema): -# code = colander.SchemaNode(colander.Integer(), description="HTTP response code") -# type = colander.SchemaNode(colander.String(), description="Response content type") -# detail = colander.SchemaNode(colander.String(), description="Response status message") -# route_name = colander.SchemaNode(colander.String(), description="Specified route") -# request_url = colander.SchemaNode(colander.String(), description="Specified url") +class NotFoundBodySchema(colander.MappingSchema): + code = colander.SchemaNode( + colander.Integer(), + description="HTTP response code", + example=404) + type = colander.SchemaNode( + colander.String(), + description="Response content type", + example="application/json") + detail = colander.SchemaNode( + colander.String(), + description="Response status message",) + route_name = colander.SchemaNode( + colander.String(), + description="Route called that generated the error", + example="/users/toto") + request_url = colander.SchemaNode( + colander.String(), + description="Request URL that generated the error", + example="http://localhost:2001/magpie/users/toto") + + +class NotFoundResponseSchema(colander.MappingSchema): + description = "The route resource could not be found." + body = NotFoundBodySchema() + + +class UnprocessableEntityResponseSchema(colander.MappingSchema): + description = "Invalid value specified." + body = BaseBodySchema(HTTPUnprocessableEntity.code) -class Version_GET_Schema(colander.MappingSchema): +class InternalServerErrorBodySchema(colander.MappingSchema): code = colander.SchemaNode( colander.Integer(), description="HTTP response code", - example=200) + example=500) + type = colander.SchemaNode( + colander.String(), + description="Response content type", + example="application/json") + detail = colander.SchemaNode( + colander.String(), + description="Response status message", + example="Internal Server Error. Unhandled exception occurred.") + route_name = colander.SchemaNode( + colander.String(), + description="Route called that generated the error", + example="/users/toto") + request_url = colander.SchemaNode( + colander.String(), + description="Request URL that generated the error", + example="http://localhost:2001/magpie/users/toto") + + +class InternalServerErrorResponseSchema(colander.MappingSchema): + description = "Internal Server Error. Unhandled exception occurred." + body = InternalServerErrorBodySchema() + + +class ProvidersListSchema(colander.SequenceSchema): + item = colander.SchemaNode( + colander.String(), + description="Available login providers.", + example=["ziggurat", "openid"], + ) + + +class ResourceTypesListSchema(colander.SequenceSchema): + item = colander.SchemaNode( + colander.String(), + description="Available resource type under root service.", + example=["file", "dictionary"], + ) + + +class GroupNamesListSchema(colander.SequenceSchema): + item = colander.SchemaNode( + colander.String(), + description="Groups the logged in user is member of", + example=["anonymous"] + ) + + +class UserNamesListSchema(colander.SequenceSchema): + item = colander.SchemaNode( + colander.String(), + description="Users registered in the db", + example=["anonymous", "admin", "toto"] + ) + + +class PermissionListSchema(colander.SequenceSchema): + item = colander.SchemaNode( + colander.String(), + description="Permissions applicable to the service/resource", + example=["read", "write"] + ) + + +class UserBodySchema(colander.MappingSchema): + user_name = colander.SchemaNode( + colander.String(), + description="Name of the user.", + example="toto") + email = colander.SchemaNode( + colander.String(), + description="Email of the user.", + example="toto@mail.com") + group_names = GroupNamesListSchema() + + +class ServiceBodySchema(colander.MappingSchema): + resource_id = colander.SchemaNode( + colander.Integer(), + description="Resource identification number", + ) + permission_names = PermissionListSchema() + service_name = colander.SchemaNode( + colander.String(), + description="Name of the service", + example="thredds" + ) + service_type = colander.SchemaNode( + colander.String(), + description="Type of the service", + example="thredds" + ) + public_url = colander.SchemaNode( + colander.String(), + description="Proxy URL available for public access with permissions", + example="http://localhost/twitcher/ows/proxy/thredds" + ) + service_url = colander.SchemaNode( + colander.String(), + description="Private URL of the service (restricted access)", + example="http://localhost:9999/thredds" + ) + + +class ResourceBodySchema(colander.MappingSchema): + resource_id = colander.SchemaNode( + colander.Integer(), + description="Resource identification number", + ) + resource_name = colander.SchemaNode( + colander.String(), + description="Name of the resource", + example="thredds" + ) + resource_type = colander.SchemaNode( + colander.String(), + description="Type of the resource", + example="service" + ) + parent_id = colander.SchemaNode( + colander.Integer(), + description="Parent resource identification number", + default=colander.null, # if no parent + missing=colander.drop # if not returned (basic_info = True) + ) + root_service_id = colander.SchemaNode( + colander.Integer(), + description="Resource tree root service identification number", + default=colander.null, # if no parent + missing=colander.drop # if not returned (basic_info = True) + ) + permission_names = PermissionListSchema() + permission_names.default = colander.null # if no parent + permission_names.missing = colander.drop # if not returned (basic_info = True) + + +# TODO: improve by making recursive resources work (?) +class Resource_ChildrenContainerWithoutChildResourceBodySchema(ResourceBodySchema): + children = colander.MappingSchema(default={}) + + +class Resource_ChildResourceWithoutChildrenBodySchema(colander.MappingSchema): + id = Resource_ChildrenContainerWithoutChildResourceBodySchema() + id.name = '{resource_id}' + + +class Resource_ChildrenContainerWithChildResourceBodySchema(ResourceBodySchema): + children = Resource_ChildResourceWithoutChildrenBodySchema() + + +class Resource_ChildResourceWithChildrenContainerBodySchema(colander.MappingSchema): + id = Resource_ChildrenContainerWithChildResourceBodySchema() + id.name = '{resource_id}' + + +class Resource_ServiceWithChildrenResourcesContainerBodySchema(ServiceBodySchema): + resources = Resource_ChildResourceWithChildrenContainerBodySchema() + + +class Resource_ServiceType_geoserverapi_SchemaNode(colander.MappingSchema): + geoserver_api = Resource_ServiceWithChildrenResourcesContainerBodySchema() + geoserver_api.name = "geoserver-api" + + +class Resource_ServiceType_ncwms_SchemaNode(colander.MappingSchema): + ncwms = Resource_ServiceWithChildrenResourcesContainerBodySchema() + + +class Resource_ServiceType_thredds_SchemaNode(colander.MappingSchema): + thredds = Resource_ServiceWithChildrenResourcesContainerBodySchema() + + +class ResourcesSchemaNode(colander.MappingSchema): + geoserver_api = Resource_ServiceType_geoserverapi_SchemaNode() + geoserver_api.name = "geoserver-api" + ncwms = Resource_ServiceType_ncwms_SchemaNode() + thredds = Resource_ServiceType_thredds_SchemaNode() + + +class Resources_ResponseBodySchema(colander.MappingSchema): + resources = ResourcesSchemaNode() + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class Resource_MatchDictCheck_ForbiddenResponseSchema(colander.MappingSchema): + description = "Resource query by id refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Resource_MatchDictCheck_NotFoundResponseSchema(colander.MappingSchema): + description = "Resource ID not found in db." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class Resource_MatchDictCheck_NotAcceptableResponseSchema(colander.MappingSchema): + description = "Resource ID is an invalid literal for `int` type." + body = BaseBodySchema(code=HTTPNotAcceptable.code) + + +class Resource_GET_ResponseBodySchema(colander.MappingSchema): + resource_id = Resource_ChildResourceWithChildrenContainerBodySchema() + resource_id.name = '{resource_id}' + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class Resource_GET_OkResponseSchema(colander.MappingSchema): + description = "Get resource successful." + body = Resource_GET_ResponseBodySchema() + + +class Resource_GET_InternalServerErrorResponseSchema(colander.MappingSchema): + description = "Failed building resource children json formatted tree." + body = InternalServerErrorBodySchema() + + +class Resource_PUT_RequestBodySchema(colander.MappingSchema): + resource_name = colander.SchemaNode( + colander.String(), + description="New name to apply to the resource to update", + ) + service_push = colander.SchemaNode( + colander.Boolean(), + description="Push service resource update to Phoenix", + missing=False, + ) + + +class Resource_PUT_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = Resource_PUT_RequestBodySchema() + + +class Resource_PUT_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + resource_id = colander.SchemaNode( + colander.String(), + description="Updated resource identification number." + ) + resource_name = colander.SchemaNode( + colander.String(), + description="Updated resource name (from object)." + ) + old_resource_name = colander.SchemaNode( + colander.String(), + description="Resource name before update." + ) + new_resource_name = colander.SchemaNode( + colander.String(), + description="Resource name after update." + ) + + +class Resource_PUT_OkResponseSchema(colander.MappingSchema): + description = "Update resource successful." + body = Resource_PUT_ResponseBodySchema() + + +class Resource_PUT_ForbiddenResponseSchema(colander.MappingSchema): + description = "Failed to update resource with new name." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Resource_DELETE_RequestBodySchema(colander.MappingSchema): + service_push = colander.SchemaNode( + colander.Boolean(), + description="Push service update to Phoenix if applicable", + missing=colander.drop, + default=False, + ) + + +class Resource_DELETE_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = Resource_DELETE_RequestBodySchema() + + +class Resource_DELETE_OkResponseSchema(colander.MappingSchema): + description = "Delete resource successful." + body = BaseBodySchema(code=HTTPOk.code) + + +class Resource_DELETE_ForbiddenResponseSchema(colander.MappingSchema): + description = "Delete resource from db failed." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Resources_GET_OkResponseSchema(colander.MappingSchema): + description = "Get resources successful." + body = Resources_ResponseBodySchema() + + +class Resources_POST_BodySchema(colander.MappingSchema): + resource_name = colander.SchemaNode( + colander.String(), + description="Name of the resource to create" + ) + resource_type = colander.SchemaNode( + colander.String(), + description="Type of the resource to create" + ) + parent_id = colander.SchemaNode( + colander.String(), + description="ID of parent resource under which the new resource should be created", + missing=colander.drop + ) + + +class Resources_POST_RequestBodySchema(colander.MappingSchema): + header = HeaderSchema() + body = Resources_POST_BodySchema() + + +class Resource_POST_ResponseBodySchema(colander.MappingSchema): + resource_id = Resource_ChildResourceWithChildrenContainerBodySchema() + resource_id.name = '{resource_id}' + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class Resources_POST_OkResponseSchema(colander.MappingSchema): + description = "Create resource successful." + body = Resource_POST_ResponseBodySchema() + + +class Resources_POST_BadRequestResponseSchema(colander.MappingSchema): + description = "Invalid [`resource_name`|`resource_type`|`parent_id`] specified for child resource creation." + body = BaseBodySchema(code=HTTPBadRequest.code) + + +class Resources_POST_ForbiddenResponseSchema(colander.MappingSchema): + description = "Failed to insert new resource in service tree using parent id." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Resources_POST_NotFoundResponseSchema(colander.MappingSchema): + description = "Could not find specified resource parent id." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class Resources_POST_ConflictResponseSchema(colander.MappingSchema): + description = "Resource name already exists at requested tree level for creation." + body = BaseBodySchema(code=HTTPConflict.code) + + +class ResourcePermissions_GET_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + permission_names = PermissionListSchema() + + +class ResourcePermissions_GET_OkResponseSchema(colander.MappingSchema): + description = "Get resource permissions successful." + body = ResourcePermissions_GET_ResponseBodySchema() + + +class ResourcePermissions_GET_NotAcceptableResponseSchema(colander.MappingSchema): + description = "Invalid resource type to extract permissions." + body = BaseBodySchema(code=HTTPNotAcceptable.code) + + +class ServiceResourcesBodySchema(ServiceBodySchema): + children = ResourcesSchemaNode() + + +class ServiceType_access_SchemaNode(colander.MappingSchema): + frontend = ServiceBodySchema(missing=colander.drop) + geoserver_web = ServiceBodySchema(missing=colander.drop) + geoserver_web.name = "geoserver-web" + magpie = ServiceBodySchema(missing=colander.drop) + + +class ServiceType_geoserverapi_SchemaNode(colander.MappingSchema): + geoserver_api = ServiceBodySchema(missing=colander.drop) + geoserver_api.name = "geoserver-api" + + +class ServiceType_geoserverwms_SchemaNode(colander.MappingSchema): + geoserverwms = ServiceBodySchema(missing=colander.drop) + + +class ServiceType_ncwms_SchemaNode(colander.MappingSchema): + ncwms = ServiceBodySchema(missing=colander.drop) + ncwms.name = "ncWMS2" + + +class ServiceType_projectapi_SchemaNode(colander.MappingSchema): + project_api = ServiceBodySchema(missing=colander.drop) + project_api.name = "project-api" + + +class ServiceType_thredds_SchemaNode(colander.MappingSchema): + thredds = ServiceBodySchema(missing=colander.drop) + + +class ServiceType_wfs_SchemaNode(colander.MappingSchema): + geoserver = ServiceBodySchema(missing=colander.drop) + + +class ServiceType_wps_SchemaNode(colander.MappingSchema): + lb_flyingpigeon = ServiceBodySchema(missing=colander.drop) + flyingpigeon = ServiceBodySchema(missing=colander.drop) + project = ServiceBodySchema(missing=colander.drop) + catalog = ServiceBodySchema(missing=colander.drop) + malleefowl = ServiceBodySchema(missing=colander.drop) + hummingbird = ServiceBodySchema(missing=colander.drop) + + +class ServicesSchemaNode(colander.MappingSchema): + access = ServiceType_access_SchemaNode() + geoserver_api = ServiceType_geoserverapi_SchemaNode(missing=colander.drop) + geoserver_api.name = "geoserver-api" + geoserverwms = ServiceType_geoserverwms_SchemaNode(missing=colander.drop) + ncwms = ServiceType_ncwms_SchemaNode() + project_api = ServiceType_projectapi_SchemaNode(missing=colander.drop) + project_api.name = "project-api" + thredds = ServiceType_thredds_SchemaNode() + wfs = ServiceType_wfs_SchemaNode(missing=colander.drop) + wps = ServiceType_wps_SchemaNode(missing=colander.drop) + + +class Service_FailureBodyResponseSchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + service_name = colander.SchemaNode( + colander.String(), + description="Service name extracted from path" + ) + + +class Service_MatchDictCheck_ForbiddenResponseSchema(colander.MappingSchema): + description = "Service query by name refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Service_MatchDictCheck_NotFoundResponseSchema(colander.MappingSchema): + description = "Service name not found in db." + body = Service_FailureBodyResponseSchema(code=HTTPNotFound.code) + + +class Service_GET_ResponseBodySchema(colander.MappingSchema): + service_name = ServiceBodySchema() + service_name.name = '{service_name}' + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class Service_GET_OkResponseSchema(colander.MappingSchema): + description = "Get service successful." + body = Service_GET_ResponseBodySchema() + + +class Services_GET_ResponseBodySchema(colander.MappingSchema): + services = ServicesSchemaNode() + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + detail.example = "Get services successful." + + +class Services_GET_OkResponseSchema(colander.MappingSchema): + description = "Get services successful." + body = Services_GET_ResponseBodySchema() + + +class Services_GET_NotAcceptableResponseSchema(colander.MappingSchema): + description = "Invalid `service_type` value does not correspond to any of the existing service types." + body = Services_GET_ResponseBodySchema() + + +class Services_POST_BodySchema(colander.MappingSchema): + service_name = colander.SchemaNode( + colander.String(), + description="Name of the service to create", + example="my_service" + ) + service_type = colander.SchemaNode( + colander.String(), + description="Type of the service to create", + example="wps" + ) + service_url = colander.SchemaNode( + colander.String(), + description="Private URL of the service to create", + example="http://localhost:9000/my_service" + ) + + +class Services_POST_RequestBodySchema(colander.MappingSchema): + header = HeaderSchema() + body = Services_POST_BodySchema() + + +class Services_POST_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class Services_POST_CreatedResponseSchema(colander.MappingSchema): + description = "Service registration to db successful." + body = Services_POST_ResponseBodySchema() + + +class Services_POST_BadRequestResponseSchema(colander.MappingSchema): + description = "Invalid `service_type` value does not correspond to any of the existing service types." + body = Services_POST_ResponseBodySchema(code=HTTPBadRequest.code) + + +class Services_POST_ForbiddenResponseSchema(colander.MappingSchema): + description = "Service registration forbidden by db." + body = Services_POST_ResponseBodySchema(code=HTTPForbidden.code) + + +class Services_POST_ConflictResponseSchema(colander.MappingSchema): + description = "Specified `service_name` value already exists." + body = Services_POST_ResponseBodySchema(code=HTTPConflict.code) + + +class Service_PUT_ResponseBodySchema(colander.MappingSchema): + service_name = colander.SchemaNode( + colander.String(), + description="New service name to apply to service specified in path", + missing=colander.drop, + default=colander.null, + example="my_service_new_name" + ) + service_url = colander.SchemaNode( + colander.String(), + description="New service private URL to apply to service specified in path", + missing=colander.drop, + default=colander.null, + example="http://localhost:9000/new_service_name" + ) + service_push = colander.SchemaNode( + colander.Boolean(), + description="Push service update to Phoenix if applicable", + missing=colander.drop, + default=False, + ) + + +class Service_PUT_RequestBodySchema(colander.MappingSchema): + header = HeaderSchema() + body = Service_PUT_ResponseBodySchema() + + +class Service_SuccessBodyResponseSchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + service = ServiceBodySchema() + + +class Service_PUT_OkResponseSchema(colander.MappingSchema): + description = "Update service successful." + body = Service_SuccessBodyResponseSchema(code=HTTPOk.code) + + +class Service_PUT_BadRequestResponseSchema(colander.MappingSchema): + description = "Logged service values are already equal to update values." + body = Service_FailureBodyResponseSchema(code=HTTPBadRequest.code) + + +class Service_PUT_ForbiddenResponseSchema(colander.MappingSchema): + description = "Update service failed during value assignment." + body = Service_FailureBodyResponseSchema(code=HTTPForbidden.code) + + +class Service_PUT_ConflictResponseSchema(colander.MappingSchema): + description = "Specified `service_name` already exists." + body = Service_FailureBodyResponseSchema(code=HTTPConflict.code) + + +# delete service use same method as direct resource delete +Service_DELETE_RequestSchema = Resource_DELETE_RequestSchema + + +class Service_DELETE_OkResponseSchema(colander.MappingSchema): + description = "Delete service successful." + body = ServiceBodySchema(code=HTTPOk.code) + + +class Service_DELETE_ForbiddenResponseSchema(colander.MappingSchema): + description = "Delete service from db refused by db." + body = Service_FailureBodyResponseSchema(code=HTTPForbidden.code) + + +class ServicePermissions_ResponseBodySchema(colander.MappingSchema): + permission_names = PermissionListSchema() + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class ServicePermissions_GET_OkResponseSchema(colander.MappingSchema): + description = "Get service permissions successful." + body = ServicePermissions_ResponseBodySchema() + + +class ServicePermissions_GET_NotAcceptableResponseSchema(colander.MappingSchema): + description = "Invalid service type specified by service." + body = ServicePermissions_ResponseBodySchema() + + +# create service's resource use same method as direct resource create +ServiceResources_POST_BodySchema = Resources_POST_BodySchema +ServiceResources_POST_RequestBodySchema = Resources_POST_RequestBodySchema +ServiceResources_POST_OkResponseSchema = Resources_POST_OkResponseSchema +ServiceResources_POST_BadRequestResponseSchema = Resources_POST_BadRequestResponseSchema +ServiceResources_POST_ForbiddenResponseSchema = Resources_POST_ForbiddenResponseSchema +ServiceResources_POST_NotFoundResponseSchema = Resources_POST_NotFoundResponseSchema +ServiceResources_POST_ConflictResponseSchema = Resources_POST_ConflictResponseSchema + + +# delete service's resource use same method as direct resource delete +ServiceResource_DELETE_RequestSchema = Resource_DELETE_RequestSchema +ServiceResource_DELETE_ForbiddenResponseSchema = Resource_DELETE_ForbiddenResponseSchema +ServiceResource_DELETE_OkResponseSchema = Resource_DELETE_OkResponseSchema + + +class ServiceResources_GET_ResponseBodySchema(colander.MappingSchema): + service_name = Resource_ServiceWithChildrenResourcesContainerBodySchema() + service_name.name = '{service_name}' + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class ServiceResources_GET_OkResponseSchema(colander.MappingSchema): + description = "Get service resources successful." + body = ServiceResources_GET_ResponseBodySchema() + + +class ServiceResourceTypes_GET_ResponseBodySchema(colander.MappingSchema): + resource_types = ResourceTypesListSchema() + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class ServiceResourceTypes_GET_OkResponseSchema(colander.MappingSchema): + description = "Get service type resource types successful." + body = ServiceResourceTypes_GET_ResponseBodySchema() + + +class ServiceResourceTypes_GET_FailureBodyResponseSchema(colander.MappingSchema): + service_type = colander.SchemaNode( + colander.String(), + description="Service type retrieved from route path." + ) + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + + +class ServiceResourceTypes_GET_ForbiddenResponseSchema(colander.MappingSchema): + description = "Failed to obtain resource types for specified service type." + body = ServiceResourceTypes_GET_FailureBodyResponseSchema(code=HTTPForbidden.code) + + +class ServiceResourceTypes_GET_NotFoundResponseSchema(colander.MappingSchema): + description = "Invalid `service_type` does not exist to obtain its resource types." + body = ServiceResourceTypes_GET_FailureBodyResponseSchema(code=HTTPNotFound.code) + + +class Users_GET_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + user_names = UserNamesListSchema() + + +class Users_GET_OkResponseSchema(colander.MappingSchema): + description = "Get users successful." + body = Users_GET_ResponseBodySchema() + + +class Users_GET_ForbiddenResponseSchema(colander.MappingSchema): + description = "Get users query refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Users_CheckInfo_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + param = ErrorVerifyParamBodySchema() + + +class Users_CheckInfo_Name_BadRequestResponseSchema(colander.MappingSchema): + description = "Invalid `user_name` value specified." + body = Users_CheckInfo_ResponseBodySchema() + + +class Users_CheckInfo_Email_BadRequestResponseSchema(colander.MappingSchema): + description = "Invalid `email` value specified." + body = Users_CheckInfo_ResponseBodySchema() + + +class Users_CheckInfo_Password_BadRequestResponseSchema(colander.MappingSchema): + description = "Invalid `password` value specified." + body = Users_CheckInfo_ResponseBodySchema() + + +class Users_CheckInfo_GroupName_BadRequestResponseSchema(colander.MappingSchema): + description = "Invalid `group_name` value specified." + body = Users_CheckInfo_ResponseBodySchema() + + +class Users_CheckInfo_Login_ConflictResponseSchema(colander.MappingSchema): + description = "Invalid `user_name` already logged in." + body = Users_CheckInfo_ResponseBodySchema() + + +class User_Check_ForbiddenResponseSchema(colander.MappingSchema): + description = "User check query was refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class User_Check_ConflictResponseSchema(colander.MappingSchema): + description = "User name matches an already existing user name." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class User_POST_RequestBodySchema(colander.MappingSchema): + user_name = colander.SchemaNode( + colander.String(), + description="New name to apply to the user", + example="john", + ) + email = colander.SchemaNode( + colander.String(), + description="New email to apply to the user", + example="john@mail.com", + ) + password = colander.SchemaNode( + colander.String(), + description="New password to apply to the user", + example="itzaseekit", + ) + group_name = colander.SchemaNode( + colander.String(), + description="New password to apply to the user", + example="users", + ) + + +class Users_POST_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = User_POST_RequestBodySchema() + + +class Users_POST_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + user = UserBodySchema() + + +class Users_POST_OkResponseSchema(colander.MappingSchema): + description = "Add user to db successful." + body = Users_POST_ResponseBodySchema() + + +class Users_POST_ForbiddenResponseSchema(colander.MappingSchema): + description = "Failed to add user to db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class UserNew_POST_ForbiddenResponseSchema(colander.MappingSchema): + description = "New user query was refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class User_PUT_RequestBodySchema(colander.MappingSchema): + user_name = colander.SchemaNode( + colander.String(), + description="New name to apply to the user", + missing=colander.drop, + example="john", + ) + email = colander.SchemaNode( + colander.String(), + description="New email to apply to the user", + missing=colander.drop, + example="john@mail.com", + ) + password = colander.SchemaNode( + colander.String(), + description="New password to apply to the user", + missing=colander.drop, + example="itzaseekit", + ) + + +class User_PUT_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = User_PUT_RequestBodySchema() + + +class Users_PUT_OkResponseSchema(colander.MappingSchema): + description = "Update user successful." + body = BaseBodySchema(code=HTTPOk.code) + + +# PUT method uses same sub-function as POST method (same responses) +User_PUT_ForbiddenResponseSchema = Users_POST_ForbiddenResponseSchema + + +class User_PUT_ConflictResponseSchema(colander.MappingSchema): + description = "New name user already exists." + body = BaseBodySchema(code=HTTPConflict.code) + + +class User_GET_ResponseBodySchema(colander.MappingSchema): + code = CodeSchemaNode + type = TypeSchemaNode + detail = DetailSchemaNode + user = UserBodySchema() + + +class User_GET_OkResponseSchema(colander.MappingSchema): + description = "Get user successful." + body = User_GET_ResponseBodySchema() + + +class User_CheckAnonymous_ForbiddenResponseSchema(colander.MappingSchema): + description = "Anonymous user query refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class User_CheckAnonymous_NotFoundResponseSchema(colander.MappingSchema): + description = "Anonymous user not found in db." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class User_GET_ForbiddenResponseSchema(colander.MappingSchema): + description = "User name query refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class User_GET_NotFoundResponseSchema(colander.MappingSchema): + description = "User name not found in db." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class User_DELETE_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = colander.MappingSchema(default={}) + + +class User_DELETE_OkResponseSchema(colander.MappingSchema): + description = "Delete user successful." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class User_DELETE_ForbiddenResponseSchema(colander.MappingSchema): + description = "Delete user by name refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class UserGroup_GET_ForbiddenResponseSchema(colander.MappingSchema): + description = "Group query was refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class UserGroup_GET_NotAcceptableResponseSchema(colander.MappingSchema): + description = "Group for new user doesn't exist." + body = BaseBodySchema(code=HTTPNotAcceptable.code) + + +class UserGroup_Check_ForbiddenResponseSchema(colander.MappingSchema): + description = "Failed to add user-group to db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class UserGroups_GET_ResponseBodySchema(BaseBodySchema): + group_names = GroupNamesListSchema() + + +class UserGroups_GET_OkResponseSchema(colander.MappingSchema): + description = "Get user groups successful." + body = UserGroups_GET_ResponseBodySchema() + + +class UserGroups_POST_RequestBodySchema(colander.MappingSchema): + user_name = colander.SchemaNode( + colander.String(), + description="Name of the user in the user-group relationship", + example="toto", + ) + group_name = colander.SchemaNode( + colander.String(), + description="Name of the group in the user-group relationship", + example="users", + ) + + +class UserGroups_POST_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = UserGroups_POST_RequestBodySchema() + + +class UserGroups_POST_ResponseBodySchema(BaseBodySchema): + user_name = colander.SchemaNode( + colander.String(), + description="Name of the user in the user-group relationship", + example="toto", + ) + group_name = colander.SchemaNode( + colander.String(), + description="Name of the group in the user-group relationship", + example="users", + ) + + +class UserGroups_POST_OkResponseSchema(colander.MappingSchema): + description = "Create user-group assignation successful." + body = UserGroups_POST_ResponseBodySchema(code=HTTPOk.code) + + +class UserGroups_POST_ConflictResponseSchema(colander.MappingSchema): + description = "User already belongs to this group." + body = BaseBodySchema(code=HTTPConflict.code) + + +class UserGroup_DELETE_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = colander.MappingSchema(default={}) + + +class UserGroup_DELETE_OkResponseSchema(colander.MappingSchema): + description = "Delete user-group successful." + body = BaseBodySchema(code=HTTPOk.code) + + +class UserGroup_DELETE_NotFoundResponseSchema(colander.MappingSchema): + description = "Invalid user-group combination for delete." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class UserResources_GET_ResponseBodySchema(BaseBodySchema): + resources = ResourcesSchemaNode() + + +class UserResources_GET_OkResponseSchema(colander.MappingSchema): + description = "Get user resources successful." + body = UserResources_GET_ResponseBodySchema() + + +class UserResources_GET_NotFoundResponseBodySchema(BaseBodySchema): + user_name = colander.SchemaNode(colander.String(), description="User name value read from path") + resource_types = ResourceTypesListSchema(description="Resource types searched for") + + +class UserResources_GET_NotFoundResponseSchema(colander.MappingSchema): + description = "Failed to populate user resources." + body = UserResources_GET_NotFoundResponseBodySchema(code=HTTPNotFound.code) + + +class UserResourcePermissions_GET_ResponseBodySchema(BaseBodySchema): + permission_names = PermissionListSchema() + + +class UserResourcePermissions_GET_OkResponseSchema(colander.MappingSchema): + description = "Get user resource permissions successful." + body = UserResourcePermissions_GET_ResponseBodySchema(code=HTTPNotFound.code) + + +class UserResourcePermissions_GET_NotAcceptableParamResponseSchema(colander.MappingSchema): + name = colander.SchemaNode(colander.String(), description='name of the parameter tested', example='resource_type') + value = colander.SchemaNode(colander.String(), description='value of the parameter tested') + compare = colander.SchemaNode(colander.String(), description='comparison value of the parameter tested', + missing=colander.drop) + + +class UserResourcePermissions_GET_NotAcceptableResponseBodySchema(colander.MappingSchema): + param = UserResourcePermissions_GET_NotAcceptableParamResponseSchema() + + +class UserResourcePermissions_GET_NotAcceptableRootServiceResponseSchema(colander.MappingSchema): + description = "Invalid `resource` specified for resource permission retrieval." + body = UserResourcePermissions_GET_NotAcceptableResponseBodySchema(code=HTTPNotAcceptable.code) + + +class UserResourcePermissions_GET_NotAcceptableResourceResponseSchema(colander.MappingSchema): + description = "Invalid `resource` specified for resource permission retrieval." + body = UserResourcePermissions_GET_NotAcceptableResponseBodySchema(code=HTTPNotAcceptable.code) + + +class UserResourcePermissions_GET_NotAcceptableResourceTypeResponseSchema(colander.MappingSchema): + description = "Invalid `resource_type` for corresponding service resource permission retrieval." + body = UserResourcePermissions_GET_NotAcceptableResponseBodySchema(code=HTTPNotAcceptable.code) + + +class UserResourcePermissions_POST_RequestBodySchema(BaseBodySchema): + resource_id = colander.SchemaNode( + colander.Integer(), + description="resource_id of the created user-resource-permission reference.") + user_id = colander.SchemaNode( + colander.Integer(), + description="user_id of the created user-resource-permission reference.") + permission_name = colander.SchemaNode( + colander.String(), + description="permission_name of the created user-resource-permission reference.") + + +class UserResourcePermissions_POST_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = UserResourcePermissions_POST_RequestBodySchema() + + +class UserResourcePermissions_POST_ResponseBodySchema(BaseBodySchema): + resource_id = colander.SchemaNode( + colander.Integer(), + description="resource_id of the created user-resource-permission reference.") + user_id = colander.SchemaNode( + colander.Integer(), + description="user_id of the created user-resource-permission reference.") + permission_name = colander.SchemaNode( + colander.String(), + description="permission_name of the created user-resource-permission reference.") + + +class UserResourcePermissions_POST_ParamResponseBodySchema(BaseBodySchema): + name = colander.SchemaNode(colander.String(), description="Specified parameter.", example=u'permission_name') + value = colander.SchemaNode(colander.String(), description="Specified parameter value.") + + +class UserResourcePermissions_POST_BadResponseBodySchema(BaseBodySchema): + resource_type = colander.SchemaNode(colander.String(), description="Specified resource_type.") + resource_name = colander.SchemaNode(colander.String(), description="Specified resource_name.") + param = UserResourcePermissions_POST_ParamResponseBodySchema() + + +class UserResourcePermissions_POST_CreatedResponseSchema(colander.MappingSchema): + description = "Create user resource permission successful." + body = UserResourcePermissions_POST_ResponseBodySchema(code=HTTPCreated.code) + + +class UserResourcePermissions_POST_BadRequestResponseSchema(colander.MappingSchema): + description = "Permission not allowed for specified `resource_type`." + body = UserResourcePermissions_POST_BadResponseBodySchema(code=HTTPNotAcceptable.code) + + +class UserResourcePermissions_POST_NotAcceptableResponseSchema(colander.MappingSchema): + description = "Failed to create permission using specified `resource_id` and `user_id`." + body = UserResourcePermissions_POST_BadResponseBodySchema(code=HTTPNotAcceptable.code) + + +class UserResourcePermissions_POST_ConflictResponseSchema(colander.MappingSchema): + description = "Permission already exist on resource for user, cannot add to db." + body = UserResourcePermissions_POST_ResponseBodySchema(code=HTTPConflict.code) + + +# using same definitions +UserResourcePermissions_DELETE_BadResponseBodySchema = UserResourcePermissions_POST_ResponseBodySchema +UserResourcePermissions_DELETE_BadRequestResponseSchema = UserResourcePermissions_POST_BadRequestResponseSchema + + +class UserResourcePermission_DELETE_RequestSchema(colander.MappingSchema): + body = colander.MappingSchema(default={}) + + +class UserResourcePermissions_DELETE_OkResponseSchema(colander.MappingSchema): + description = "Delete user resource permission successful." + body = BaseBodySchema(code=HTTPOk.code) + + +class UserResourcePermissions_DELETE_NotFoundResponseSchema(colander.MappingSchema): + description = "Could not find user resource permission to delete from db." + body = UserResourcePermissions_DELETE_BadResponseBodySchema(code=HTTPOk.code) + + +class UserServiceResources_GET_ResponseBodySchema(colander.MappingSchema): + service = ServiceResourcesBodySchema() + + +class UserServiceResources_GET_OkResponseSchema(BaseBodySchema): + description = "Get user service resources successful." + body = UserServiceResources_GET_ResponseBodySchema() + + +class UserServicePermissions_POST_RequestBodySchema(colander.MappingSchema): + permission_name = colander.SchemaNode(colander.String(), description="Name of the permission to create.") + + +class UserServicePermissions_POST_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = UserServicePermissions_POST_RequestBodySchema() + + +class UserServicePermission_DELETE_RequestSchema(colander.MappingSchema): + header = HeaderSchema() + body = colander.MappingSchema(default={}) + + +class UserServices_GET_ResponseBodySchema(BaseBodySchema): + services = ServicesSchemaNode() + + +class UserServices_GET_OkResponseSchema(colander.MappingSchema): + description = "Get user services successful." + body = UserServices_GET_ResponseBodySchema + + +class UserServicePermissions_GET_ResponseBodySchema(BaseBodySchema): + permission_names = PermissionListSchema() + + +class UserServicePermissions_GET_OkResponseSchema(colander.MappingSchema): + description = "Get user service permissions successful." + body = UserServicePermissions_GET_ResponseBodySchema() + + +class UserServicePermissions_GET_NotFoundResponseSchema(colander.MappingSchema): + description = "Could not find permissions using specified `service_name` and `user_name`." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class Group_MatchDictCheck_ForbiddenResponseSchema(colander.MappingSchema): + description = "Group query by name refused by db." + body = BaseBodySchema(code=HTTPForbidden.code) + + +class Group_MatchDictCheck_NotFoundResponseSchema(colander.MappingSchema): + description = "Group name not found in db." + body = BaseBodySchema(code=HTTPNotFound.code) + + +class GroupServiceResources_GET_ResponseBodySchema(BaseBodySchema): + service = ServiceResourcesBodySchema() + + +class GroupServiceResources_GET_OkResponseSchema(colander.MappingSchema): + description = "Get group service resources successful." + body = GroupServiceResources_GET_ResponseBodySchema() + + +class Session_GET_ResponseBodySchema(BaseBodySchema): + user = UserBodySchema(missing=colander.drop) + authenticated = colander.SchemaNode( + colander.Boolean(), + description="Indicates if any user session is currently authenticated (user logged in).") + + +class Session_GET_OkResponseSchema(colander.MappingSchema): + description = "Get session successful." + body = Session_GET_ResponseBodySchema() + + +class Session_GET_InternalServerErrorResponseSchema(colander.MappingSchema): + description = "Failed to get session details." + body = InternalServerErrorResponseSchema() + + +class Providers_GET_ResponseBodySchema(colander.MappingSchema): + provider_names = ProvidersListSchema() + internal_providers = ProvidersListSchema() + external_providers = ProvidersListSchema() + + +class Providers_GET_OkResponseSchema(BaseBodySchema): + description = "Get providers successful." + body = Providers_GET_ResponseBodySchema() + + +class Version_GET_ResponseBodySchema(colander.MappingSchema): + code = colander.SchemaNode( + colander.Integer(), + description="HTTP response code", + example=HTTPOk.code) type = colander.SchemaNode( colander.String(), description="Response content type", @@ -103,7 +1512,7 @@ class Version_GET_Schema(colander.MappingSchema): version = colander.SchemaNode( colander.String(), description="Magpie version string", - example=__version__) + example=__meta__.__version__) db_version = colander.SchemaNode( colander.String(), description="Database version string", @@ -111,28 +1520,286 @@ class Version_GET_Schema(colander.MappingSchema): class Version_GET_OkResponseSchema(colander.MappingSchema): - body = Version_GET_Schema() - - -# NOT REQUIRED field -#field = colader.SchemaNode(colander.String(), missing=colader.drop) + description = "Get version successful." + body = Version_GET_ResponseBodySchema() -# Responses Schemas -###HTTP200_User_ResponseSchema +# Responses for specific views +Resource_GET_responses = { + '200': Resource_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Resource_MatchDictCheck_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': Resource_MatchDictCheck_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), + '500': Resource_GET_InternalServerErrorResponseSchema() +} +Resource_PUT_responses = { + '200': Resource_PUT_OkResponseSchema(), + '403': Resource_PUT_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': Resource_MatchDictCheck_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +Resources_GET_responses = { + '200': Resources_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '500': Resource_GET_InternalServerErrorResponseSchema() +} +Resources_POST_responses = { + '200': Resources_POST_OkResponseSchema(), + '400': Resources_POST_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Resources_POST_ForbiddenResponseSchema(), + '404': Resources_POST_NotFoundResponseSchema(), + '409': Resources_POST_ConflictResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +Resources_DELETE_responses = { + '200': Resource_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Resource_DELETE_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': Resource_MatchDictCheck_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +ResourcePermissions_GET_responses = { + '200': ResourcePermissions_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Resource_MatchDictCheck_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': ResourcePermissions_GET_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +ServiceTypes_GET_responses = { + '200': Services_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '406': Services_GET_NotAcceptableResponseSchema(), +} +Services_GET_responses = { + '200': Services_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '406': Services_GET_NotAcceptableResponseSchema(), +} +Services_POST_responses = { + '201': Services_POST_CreatedResponseSchema(), + '400': Services_POST_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Services_POST_ForbiddenResponseSchema(), + '409': Services_POST_ConflictResponseSchema(), +} +Service_GET_responses = { + '200': Service_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Service_MatchDictCheck_ForbiddenResponseSchema(), + '404': Service_MatchDictCheck_NotFoundResponseSchema(), +} +Service_PUT_responses = { + '200': Service_PUT_OkResponseSchema(), + '400': Service_PUT_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Service_PUT_ForbiddenResponseSchema(), + '409': Service_PUT_ConflictResponseSchema(), +} +Service_DELETE_responses = { + '200': Service_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Service_DELETE_ForbiddenResponseSchema(), + '404': Service_MatchDictCheck_NotFoundResponseSchema(), +} +ServicePermissions_GET_responses = { + '200': ServicePermissions_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Service_MatchDictCheck_ForbiddenResponseSchema(), + '404': Service_MatchDictCheck_NotFoundResponseSchema(), + '406': ServicePermissions_GET_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +ServiceResources_GET_responses = { + '200': ServiceResources_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Service_MatchDictCheck_ForbiddenResponseSchema(), + '404': Service_MatchDictCheck_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +ServiceResources_POST_responses = { + '200': ServiceResources_POST_OkResponseSchema(), + '400': ServiceResources_POST_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': ServiceResources_POST_ForbiddenResponseSchema(), + '404': ServiceResources_POST_NotFoundResponseSchema(), + '409': ServiceResources_POST_ConflictResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +ServiceResource_GET_responses = { + '200': ServiceResourceTypes_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': ServiceResourceTypes_GET_ForbiddenResponseSchema(), + '404': ServiceResourceTypes_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +ServiceResource_DELETE_responses = { + '200': ServiceResource_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': ServiceResource_DELETE_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': Resource_MatchDictCheck_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserResources_GET_responses = { + '200': UserResources_GET_OkResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': UserResources_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserGroups_GET_responses = { + '200': UserGroups_GET_OkResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserGroups_POST_responses = { + '200': UserGroups_POST_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '409': UserGroups_POST_ConflictResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserGroup_DELETE_responses = { + '200': UserGroup_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserResourcePermissions_GET_responses = { + '200': UserResourcePermissions_GET_OkResponseSchema(), + '403': Resource_MatchDictCheck_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': Resource_MatchDictCheck_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserResourcePermissions_POST_responses = { + '201': UserResourcePermissions_POST_CreatedResponseSchema(), + '400': UserResourcePermissions_POST_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '406': UserResourcePermissions_POST_NotAcceptableResponseSchema(), + '409': UserResourcePermissions_POST_ConflictResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserResourcePermission_DELETE_responses = { + '200': UserResourcePermissions_DELETE_OkResponseSchema(), + '400': UserResourcePermissions_DELETE_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '404': UserResourcePermissions_DELETE_NotFoundResponseSchema(), + '406': UserResourcePermissions_GET_NotAcceptableResourceResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserServices_GET_responses = { + '200': UserServices_GET_OkResponseSchema(), + '403': User_GET_ForbiddenResponseSchema(), + '404': User_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserServicePermissions_GET_responses = { + '200': UserServicePermissions_GET_OkResponseSchema(), + '403': User_GET_ForbiddenResponseSchema(), + '404': UserServicePermissions_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserServiceResources_GET_responses = { + '200': UserServiceResources_GET_OkResponseSchema(), + '403': User_GET_ForbiddenResponseSchema(), + '404': Service_MatchDictCheck_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +UserServicePermissions_POST_responses = UserResourcePermissions_POST_responses +UserServicePermission_DELETE_responses = UserResourcePermission_DELETE_responses +LoggedUserResources_GET_responses = { + '200': UserResources_GET_OkResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': UserResources_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserGroups_GET_responses = { + '200': UserGroups_GET_OkResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserGroups_POST_responses = { + '200': UserGroups_POST_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '409': UserGroups_POST_ConflictResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserGroup_DELETE_responses = { + '200': UserGroup_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserResourcePermissions_GET_responses = { + '200': UserResourcePermissions_GET_OkResponseSchema(), + '403': Resource_MatchDictCheck_ForbiddenResponseSchema(), + '404': Resource_MatchDictCheck_NotFoundResponseSchema(), + '406': Resource_MatchDictCheck_NotAcceptableResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserResourcePermissions_POST_responses = { + '201': UserResourcePermissions_POST_CreatedResponseSchema(), + '400': UserResourcePermissions_POST_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '406': UserResourcePermissions_POST_NotAcceptableResponseSchema(), + '409': UserResourcePermissions_POST_ConflictResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserResourcePermission_DELETE_responses = { + '200': UserResourcePermissions_DELETE_OkResponseSchema(), + '400': UserResourcePermissions_DELETE_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '404': UserResourcePermissions_DELETE_NotFoundResponseSchema(), + '406': UserResourcePermissions_GET_NotAcceptableResourceResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserServices_GET_responses = { + '200': UserServices_GET_OkResponseSchema(), + '403': User_GET_ForbiddenResponseSchema(), + '404': User_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserServicePermissions_GET_responses = { + '200': UserServicePermissions_GET_OkResponseSchema(), + '403': User_GET_ForbiddenResponseSchema(), + '404': UserServicePermissions_GET_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserServiceResources_GET_responses = { + '200': UserServiceResources_GET_OkResponseSchema(), + '403': User_GET_ForbiddenResponseSchema(), + '404': Service_MatchDictCheck_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +} +LoggedUserServicePermissions_POST_responses = LoggedUserResourcePermissions_POST_responses +LoggedUserServicePermission_DELETE_responses = LoggedUserResourcePermission_DELETE_responses +Version_GET_responses = { + '200': Version_GET_OkResponseSchema() +} -#class OkResponseSchema(colander.MappingSchema): -# body = BaseSchema() - -# return JSON Swagger specifications of Magpie REST API on route '/magpie/__api__' -# using all Cornice Services and Schemas -@SwaggerAPI.get(tags=[APITag]) -def api_spec(request): +# use Cornice Services and Schemas to return swagger specifications +def api_schema(request): + """ + Return JSON Swagger specifications of Magpie REST API. + """ generator = CorniceSwagger(get_services()) - json_api_spec = generator('Magpie REST API', __version__) + # function docstrings are used to create the route's summary in Swagger-UI + generator.summary_docstrings = True + generator.default_security = get_security + generator.swagger = SecurityDefinitionAPI + json_api_spec = generator.generate(title=TitleAPI, version=__meta__.__version__, info=InfoAPI, base_path='/magpie') return json_api_spec - - -#def openapi_spec_generate_json(request): -# api_json = requests.post() diff --git a/magpie/api/esgf/esgfopenid.py b/magpie/api/esgf/esgfopenid.py index 65b24de23..511078b18 100644 --- a/magpie/api/esgf/esgfopenid.py +++ b/magpie/api/esgf/esgfopenid.py @@ -7,8 +7,8 @@ This providers are dependent on the |pyopenid|_ package. """ -import urllib2 import ssl +from six.moves.urllib.request import urlopen from authomatic.providers.openid import OpenID from openid.fetchers import setDefaultFetcher, Urllib2Fetcher @@ -21,10 +21,8 @@ class MyFetcher(Urllib2Fetcher): @staticmethod - def _urlopen(req): - return urllib2.urlopen(req, context=ssl._create_unverified_context()) - - urlopen = _urlopen + def urlopen(req): + return urlopen(req, context=ssl._create_unverified_context()) class ESGFOpenID(OpenID): diff --git a/magpie/api/login/login.py b/magpie/api/login/login.py index 9c2e4cb3d..340058e82 100644 --- a/magpie/api/login/login.py +++ b/magpie/api/login/login.py @@ -3,12 +3,14 @@ from authomatic import Authomatic, provider_id from security import authomatic -from definitions.pyramid_definitions import * -from definitions.ziggurat_definitions import * +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.ziggurat_definitions import * from magpie import * -from api.api_requests import * -from api.api_except import * -from api.management.user.user_utils import create_user +from magpie.api.api_except import * +from magpie.api.api_requests import * +from magpie.api.api_rest_schemas import * +from magpie.api.management.user.user_formats import * +from magpie.api.management.user.user_utils import create_user import requests @@ -49,9 +51,10 @@ def sign_in_external(request): provider_name = get_value_multiformat_post_checked(request, 'provider_name') user_name = get_value_multiformat_post_checked(request, 'user_name') - verify_param(provider_name, paramCompare=providers, isIn=True, httpError=HTTPNotAcceptable, - msgOnFail="Invalid: `provider_name` not found within available providers.", - content={u'provider_name': str(provider_name), u'providers': providers}) + verify_param(provider_name, paramName=u'provider_name', paramCompare=providers, isIn=True, + httpError=HTTPNotAcceptable, content={u'provider_name': str(provider_name), u'providers': providers}, + msgOnFail="Invalid: `provider_name` not found within available providers.") + if provider_name == 'openid': query_field = dict(id=user_name) elif provider_name == 'github': @@ -68,13 +71,14 @@ def sign_in_external(request): @view_config(route_name='signin', request_method='POST', permission=NO_PERMISSION_REQUIRED) def sign_in(request): + """Signs in a user session.""" provider_name = get_value_multiformat_post_checked(request, 'provider_name') user_name = get_value_multiformat_post_checked(request, 'user_name') password = get_multiformat_post(request, 'password') # no check since password is None for external login# - verify_param(provider_name, paramCompare=providers, isIn=True, httpError=HTTPNotAcceptable, - msgOnFail="Invalid `provider_name` not found within available providers", - content={u'provider_name': str(provider_name), u'providers': providers}) + verify_param(provider_name, paramName=u'provider_name', paramCompare=providers, isIn=True, + httpError=HTTPNotAcceptable, content={u'provider_name': str(provider_name), u'providers': providers}, + msgOnFail="Invalid `provider_name` not found within available providers") if provider_name in internal_providers: signin_internal_url = '{}/signin_internal'.format(request.application_url) @@ -103,7 +107,7 @@ def login_success_ziggurat(request): def new_user_external(external_user_name, external_id, email, provider_name, db_session): - """create new user with an External Identity""" + """Create new user with an External Identity""" local_user_name = external_user_name + '_' + provider_name local_user_name = local_user_name.replace(" ", '_') create_user(local_user_name, password=None, email=email, group_name=USER_GROUP, db_session=db_session) @@ -158,7 +162,8 @@ def login_failure(request, reason=None): @view_config(context=ZigguratSignOut, permission=NO_PERMISSION_REQUIRED) -def sign_out_ziggurat(request): +def sign_out(request): + """Signs out the current user session.""" return valid_http(httpSuccess=HTTPOk, detail="Sign out successful.", httpKWArgs={'headers': forget(request)}) @@ -204,29 +209,32 @@ def authomatic_login(request): return response +@SessionAPI.get(schema=Session_GET_ResponseBodySchema(), tags=[LoginTag], response_schemas={ + '200': Session_GET_OkResponseSchema(), + '500': Session_GET_InternalServerErrorResponseSchema() +}) @view_config(route_name='session', permission=NO_PERMISSION_REQUIRED) def get_session(request): + """Get information about current session.""" def _get_session(req): authn_policy = req.registry.queryUtility(IAuthenticationPolicy) principals = authn_policy.effective_principals(req) if Authenticated in principals: user = request.user - json_resp = {u'authenticated': True, - u'user_name': user.user_name, - u'user_email': user.email, - u'group_names': [group.group_name for group in user.groups]} + json_resp = {u'authenticated': True, u'user': format_user(user)} else: json_resp = {u'authenticated': False} return json_resp session_json = evaluate_call(lambda: _get_session(request), httpError=HTTPInternalServerError, - msgOnFail="Failed to get session details") - return valid_http(httpSuccess=HTTPOk, detail="Get session successful", content=session_json) + msgOnFail=Session_GET_InternalServerErrorResponseSchema.description) + return valid_http(httpSuccess=HTTPOk, detail=Session_GET_OkResponseSchema.description, content=session_json) @view_config(route_name='providers', request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_providers(request): - return valid_http(httpSuccess=HTTPOk, detail="Get providers successful", - content={u'provider_names': providers, - u'internal_providers': internal_providers, - u'external_providers': external_providers}) + """Get list of login providers.""" + return valid_http(httpSuccess=HTTPOk, detail=Providers_GET_OkResponseSchema.description, + content={u'provider_names': sorted(providers), + u'internal_providers': sorted(internal_providers), + u'external_providers': sorted(external_providers)}) diff --git a/magpie/api/management/__init__.py b/magpie/api/management/__init__.py index ec4699c8a..0b8fbadbe 100644 --- a/magpie/api/management/__init__.py +++ b/magpie/api/management/__init__.py @@ -1,5 +1,5 @@ def includeme(config): - config.include('api.management.group') - config.include('api.management.user') - config.include('api.management.service') - config.include('api.management.resource') + config.include('magpie.api.management.group') + config.include('magpie.api.management.user') + config.include('magpie.api.management.service') + config.include('magpie.api.management.resource') diff --git a/magpie/api/management/group/__init__.py b/magpie/api/management/group/__init__.py index bd62cd4d2..f9f298443 100644 --- a/magpie/api/management/group/__init__.py +++ b/magpie/api/management/group/__init__.py @@ -1,3 +1,4 @@ +from magpie.api.api_rest_schemas import * import logging logger = logging.getLogger(__name__) @@ -5,17 +6,16 @@ def includeme(config): logger.info('Adding api group ...') # Add all the rest api routes - config.add_route('groups', '/groups') - config.add_route('group', '/groups/{group_name}') - config.add_route('group_users', '/groups/{group_name}/users') - config.add_route('group_services', '/groups/{group_name}/services') - config.add_route('group_service_permissions', '/groups/{group_name}/services/{service_name}/permissions') - config.add_route('group_service_permission', '/groups/{group_name}/services/{service_name}/permissions/{permission_name}') - config.add_route('group_service_resources', '/groups/{group_name}/services/{service_name}/resources') - - config.add_route('group_resources', '/groups/{group_name}/resources') - config.add_route('group_resource_permissions', '/groups/{group_name}/resources/{resource_id}/permissions') - config.add_route('group_resource_permission', '/groups/{group_name}/resources/{resource_id}/permissions/{permission_name}') - config.add_route('group_resources_type', '/groups/{group_name}/resources/types/{resource_type}') + config.add_route(**service_api_route_info(GroupsAPI)) + config.add_route(**service_api_route_info(GroupAPI)) + config.add_route(**service_api_route_info(GroupUsersAPI)) + config.add_route(**service_api_route_info(GroupServicesAPI)) + config.add_route(**service_api_route_info(GroupServicePermissionsAPI)) + config.add_route(**service_api_route_info(GroupServicePermissionAPI)) + config.add_route(**service_api_route_info(GroupServiceResourcesAPI)) + config.add_route(**service_api_route_info(GroupResourcesAPI)) + config.add_route(**service_api_route_info(GroupResourcePermissionsAPI)) + config.add_route(**service_api_route_info(GroupResourcePermissionAPI)) + config.add_route(**service_api_route_info(GroupResourceTypesAPI)) config.scan() diff --git a/magpie/api/management/group/group_formats.py b/magpie/api/management/group/group_formats.py index 92fa73355..a997f77e1 100644 --- a/magpie/api/management/group/group_formats.py +++ b/magpie/api/management/group/group_formats.py @@ -1,4 +1,4 @@ -from api.api_requests import * +from magpie.api.api_requests import * def format_group(group, basic_info=False): @@ -18,5 +18,5 @@ def fmt_grp(grp, info): return evaluate_call( lambda: fmt_grp(group, basic_info), httpError=HTTPInternalServerError, - msgOnFail="Failed to format group", content={u'group': repr(group)} + msgOnFail="Failed to format group.", content={u'group': repr(group)} ) diff --git a/magpie/api/management/group/group_utils.py b/magpie/api/management/group/group_utils.py index bc18e28d1..b714c3bd1 100644 --- a/magpie/api/management/group/group_utils.py +++ b/magpie/api/management/group/group_utils.py @@ -1,12 +1,12 @@ from models import resource_tree_service, resource_type_dict from services import service_type_dict -from api.api_requests import * -from api.api_except import * -from api.management.resource.resource_utils import check_valid_service_resource_permission -from api.management.resource.resource_formats import format_resource -from api.management.service.service_formats import format_service_resources, format_service -from api.management.group.group_formats import format_group -from definitions.ziggurat_definitions import * +from magpie.api.api_requests import * +from magpie.api.api_except import * +from magpie.api.management.resource.resource_utils import check_valid_service_resource_permission +from magpie.api.management.resource.resource_formats import format_resource +from magpie.api.management.service.service_formats import format_service_resources, format_service +from magpie.api.management.group.group_formats import format_group +from magpie.definitions.ziggurat_definitions import * def get_all_groups(db_session): diff --git a/magpie/api/management/group/group_views.py b/magpie/api/management/group/group_views.py index e77fa076d..20816ea30 100644 --- a/magpie/api/management/group/group_views.py +++ b/magpie/api/management/group/group_views.py @@ -1,16 +1,18 @@ -from api.management.group.group_utils import * -from definitions.ziggurat_definitions import * -from definitions.pyramid_definitions import view_config +from magpie.api.management.group.group_utils import * +from magpie.definitions.ziggurat_definitions import * +from magpie.definitions.pyramid_definitions import view_config -@view_config(route_name='groups', request_method='GET') +@view_config(route_name=GroupsAPI.name, request_method='GET') def get_groups(request): + """Get list of group names.""" group_names = get_all_groups(request.db) return valid_http(httpSuccess=HTTPOk, detail="Get groups successful", content={u'group_names': group_names}) -@view_config(route_name='groups', request_method='POST') +@view_config(route_name=GroupsAPI.name, request_method='POST') def create_group(request): + """Create a group.""" group_name = get_value_multiformat_post_checked(request, 'group_name') group = GroupService.by_group_name(group_name, db_session=request.db) group_content_error = {u'group_name': str(group_name)} @@ -26,8 +28,9 @@ def create_group(request): content={u'group': format_group(new_group)}) -@view_config(route_name='group', request_method='PUT') +@view_config(route_name=GroupAPI.name, request_method='PUT') def edit_group(request): + """Update a group by name.""" group = get_group_matchdict_checked(request, group_name_key='group_name') new_group_name = get_multiformat_post(request, 'group_name') verify_param(new_group_name, notNone=True, notEmpty=True, httpError=HTTPNotAcceptable, @@ -44,24 +47,28 @@ def edit_group(request): return valid_http(httpSuccess=HTTPOk, detail="Update group successful.") -@view_config(route_name='group', request_method='DELETE') +@view_config(route_name=GroupAPI.name, request_method='DELETE') def delete_group(request): + """Delete a group by name.""" group = get_group_matchdict_checked(request) evaluate_call(lambda: request.db.delete(group), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, msgOnFail="Delete group forbidden by db") return valid_http(httpSuccess=HTTPOk, detail="Delete group successful") -@view_config(route_name='group_users', request_method='GET') +@view_config(route_name=GroupUsersAPI.name, request_method='GET') def get_group_users(request): + """List all user from a group.""" group = get_group_matchdict_checked(request) user_names = evaluate_call(lambda: [user.user_name for user in group.users], httpError=HTTPForbidden, msgOnFail="Failed to obtain group user names from db") - return valid_http(httpSuccess=HTTPOk, detail="Get group users successful", content={u'user_names': user_names}) + return valid_http(httpSuccess=HTTPOk, detail="Get group users successful", + content={u'user_names': sorted(user_names)}) -@view_config(route_name='group_services', request_method='GET') +@view_config(route_name=GroupServicesAPI.name, request_method='GET') def get_group_services_view(request): + """List all services a group has permission on.""" group = get_group_matchdict_checked(request) res_perm_dict = get_group_resources_permissions_dict(group, resource_types=[u'service'], db_session=request.db) grp_svc_json = evaluate_call(lambda: get_group_services(res_perm_dict, request.db), @@ -70,8 +77,9 @@ def get_group_services_view(request): return valid_http(httpSuccess=HTTPOk, detail="Get group services successful", content={u'services': grp_svc_json}) -@view_config(route_name='group_service_permissions', request_method='GET') +@view_config(route_name=GroupServicePermissionsAPI.name, request_method='GET') def get_group_service_permissions_view(request): + """List all permissions a group has on a specific service.""" group = get_group_matchdict_checked(request) service = get_service_matchdict_checked(request) svc_perms_found = evaluate_call(lambda: get_group_service_permissions(group, service, request.db), @@ -82,24 +90,27 @@ def get_group_service_permissions_view(request): content={u'permission_names': svc_perms_found}) -@view_config(route_name='group_service_permissions', request_method='POST') +@view_config(route_name=GroupServicePermissionsAPI.name, request_method='POST') def create_group_service_permission(request): + """Create a permission on a specific resource for a group.""" group = get_group_matchdict_checked(request) service = get_service_matchdict_checked(request) perm_name = get_permission_multiformat_post_checked(request, service) return create_group_resource_permission(perm_name, service, group, db_session=request.db) -@view_config(route_name='group_service_permission', request_method='DELETE') +@view_config(route_name=GroupServicePermissionAPI.name, request_method='DELETE') def delete_group_service_permission(request): + """Delete a permission from a specific resource for a group.""" group = get_group_matchdict_checked(request) service = get_service_matchdict_checked(request) perm_name = get_permission_matchdict_checked(request, service) return delete_group_resource_permission(perm_name, service, group, db_session=request.db) -@view_config(route_name='group_resources', request_method='GET') +@view_config(route_name=GroupResourcesAPI.name, request_method='GET') def get_group_resources_view(request): + """List all resources a group has permission on.""" group = get_group_matchdict_checked(request) grp_res_json = evaluate_call(lambda: get_group_resources(group, request.db), fallback=lambda: request.db.rollback(), httpError=HTTPInternalServerError, content={u'group': repr(group)}, @@ -107,8 +118,9 @@ def get_group_resources_view(request): return valid_http(httpSuccess=HTTPOk, detail="Get group resources successful", content={u'resources': grp_res_json}) -@view_config(route_name='group_resource_permissions', request_method='GET') +@view_config(route_name=GroupResourcePermissionsAPI.name, request_method='GET') def get_group_resource_permissions_view(request): + """List all permissions a group has on a specific resource.""" group = get_group_matchdict_checked(request) resource = get_resource_matchdict_checked(request) perm_names = get_group_resource_permissions(group, resource, db_session=request.db) @@ -116,24 +128,34 @@ def get_group_resource_permissions_view(request): content={u'permission_names': perm_names}) -@view_config(route_name='group_resource_permissions', request_method='POST') +@view_config(route_name=GroupResourcePermissionsAPI.name, request_method='POST') def create_group_resource_permission_view(request): + """Create a permission on a specific resource for a group.""" group = get_group_matchdict_checked(request) resource = get_resource_matchdict_checked(request) perm_name = get_permission_multiformat_post_checked(request, resource) return create_group_resource_permission(perm_name, resource, group, db_session=request.db) -@view_config(route_name='group_resource_permission', request_method='DELETE') +@view_config(route_name=GroupResourcePermissionAPI.name, request_method='DELETE') def delete_group_resource_permission_view(request): + """Delete a permission from a specific resource for a group.""" group = get_group_matchdict_checked(request) resource = get_resource_matchdict_checked(request) perm_name = get_permission_matchdict_checked(request, resource) return delete_group_resource_permission(perm_name, resource, group, db_session=request.db) -@view_config(route_name='group_service_resources', request_method='GET') +@GroupServiceResourcesAPI.get(tags=[GroupsTag], response_schemas={ + '200': GroupServiceResources_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Group_MatchDictCheck_ForbiddenResponseSchema(), + '404': Group_MatchDictCheck_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema() +}) +@view_config(route_name=GroupServiceResourcesAPI.name, request_method='GET') def get_group_service_resources_view(request): + """List all resources under a service a group has permission on.""" group = get_group_matchdict_checked(request) service = get_service_matchdict_checked(request) svc_perms = get_group_service_permissions(group=group, service=service, db_session=request.db) @@ -145,5 +167,5 @@ def get_group_service_resources_view(request): resources_perms_dict=res_perms, display_all=False ) - return valid_http(httpSuccess=HTTPOk, detail="Get group service resources successful", + return valid_http(httpSuccess=HTTPOk, detail=GroupServiceResources_GET_OkResponseSchema.description, content={u'service': svc_res_json}) diff --git a/magpie/api/management/resource/__init__.py b/magpie/api/management/resource/__init__.py index b390d4f9f..9e6e96e21 100644 --- a/magpie/api/management/resource/__init__.py +++ b/magpie/api/management/resource/__init__.py @@ -1,3 +1,4 @@ +from magpie.api.api_rest_schemas import * import logging logger = logging.getLogger(__name__) @@ -6,8 +7,8 @@ def includeme(config): logger.info('Adding api resource ...') # Add all the rest api routes - config.add_route('resources', '/resources') - config.add_route('resource', '/resources/{resource_id}') - config.add_route('resource_permissions', '/resources/{resource_id}/permissions') + config.add_route(**service_api_route_info(ResourcesAPI)) + config.add_route(**service_api_route_info(ResourceAPI)) + config.add_route(**service_api_route_info(ResourcePermissionsAPI)) config.scan() diff --git a/magpie/api/management/resource/resource_formats.py b/magpie/api/management/resource/resource_formats.py index 414768feb..c7cf11a25 100644 --- a/magpie/api/management/resource/resource_formats.py +++ b/magpie/api/management/resource/resource_formats.py @@ -1,7 +1,7 @@ -from definitions.pyramid_definitions import * +from magpie.definitions.pyramid_definitions import * from models import resource_tree_service, Service from services import service_type_dict -from api.api_except import evaluate_call +from magpie.api.api_except import evaluate_call def format_resource(resource, permissions=None, basic_info=False): @@ -19,14 +19,14 @@ def fmt_res(res, perms, info): u'parent_id': res.parent_id, u'root_service_id': res.root_service_id, u'children': {}, - u'permission_names': list() if perms is None else perms + u'permission_names': list() if perms is None else sorted(perms) } return evaluate_call( lambda: fmt_res(resource, permissions, basic_info), httpError=HTTPInternalServerError, - msgOnFail="Failed to format resource", - content={u'service': repr(resource), u'permissions': repr(permissions), u'basic_info': str(basic_info)} + msgOnFail="Failed to format resource.", + content={u'resource': repr(resource), u'permissions': repr(permissions), u'basic_info': str(basic_info)} ) diff --git a/magpie/api/management/resource/resource_utils.py b/magpie/api/management/resource/resource_utils.py index f0fb59eab..c1499a9cf 100644 --- a/magpie/api/management/resource/resource_utils.py +++ b/magpie/api/management/resource/resource_utils.py @@ -1,10 +1,14 @@ import models +from common import * from models import resource_factory, resource_type_dict, resource_tree_service from services import service_type_dict -from definitions.pyramid_definitions import * -from definitions.ziggurat_definitions import * -from api.api_except import verify_param, evaluate_call, raise_http, valid_http -from api.management.resource.resource_formats import format_resource +from register import sync_services_phoenix +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.ziggurat_definitions import * +from magpie.api.api_rest_schemas import * +from magpie.api.api_requests import * +from magpie.api.api_except import verify_param, evaluate_call, raise_http, valid_http +from magpie.api.management.resource.resource_formats import format_resource def check_valid_service_resource_permission(permission_name, service_resource, db_session): @@ -18,8 +22,10 @@ def check_valid_service_resource_permission(permission_name, service_resource, d svc_res_perms = get_resource_permissions(service_resource, db_session=db_session) svc_res_type = service_resource.resource_type svc_res_name = service_resource.resource_name - verify_param(permission_name, paramCompare=svc_res_perms, isIn=True, httpError=HTTPBadRequest, - msgOnFail="Permission not allowed for {0} `{1}`".format(svc_res_type, svc_res_name)) + verify_param(permission_name, paramName=u'permission_name', paramCompare=svc_res_perms, isIn=True, + httpError=HTTPBadRequest, + content={u'resource_type': str(svc_res_type), u'resource_name': str(svc_res_name)}, + msgOnFail=UserResourcePermissions_POST_BadRequestResponseSchema.description) def check_valid_service_resource(parent_resource, resource_type, db_session): @@ -39,13 +45,14 @@ def check_valid_service_resource(parent_resource, resource_type, db_session): root_service = get_resource_root_service(parent_resource, db_session=db_session) verify_param(root_service, notNone=True, httpError=HTTPInternalServerError, msgOnFail="Failed retrieving `root_service` from db") - verify_param(root_service.resource_type, isEqual=True, httpError=HTTPInternalServerError, paramCompare=u'service', + verify_param(root_service.resource_type, isEqual=True, httpError=HTTPInternalServerError, + paramName=u'resource_type', paramCompare=u'service', msgOnFail="Invalid `root_service` retrieved from db is not a service") verify_param(service_type_dict[root_service.type].child_resource_allowed, isEqual=True, paramCompare=True, httpError=HTTPNotAcceptable, msgOnFail="Child resource not allowed for specified service type `{}`".format(root_service.type)) verify_param(resource_type, isIn=True, httpError=HTTPNotAcceptable, - paramCompare=service_type_dict[root_service.type].resource_types, + paramName=u'resource_type', paramCompare=service_type_dict[root_service.type].resource_types, msgOnFail="Invalid `resource_type` specified for service type `{}`".format(root_service.type)) return root_service @@ -84,8 +91,8 @@ def get_service_or_resource_types(service_resource): def get_resource_permissions(resource, db_session): - verify_param(resource, notNone=True, httpError=HTTPNotAcceptable, - msgOnFail="Invalid `resource` specified for resource permission retrieval") + verify_param(resource, notNone=True, httpError=HTTPNotAcceptable, paramName=u'resource', + msgOnFail=UserResourcePermissions_GET_NotAcceptableResourceResponseSchema.description) # directly access the service resource if resource.root_service_id is None: service = resource @@ -93,12 +100,13 @@ def get_resource_permissions(resource, db_session): # otherwise obtain root level service to infer sub-resource permissions service = models.Service.by_resource_id(resource.root_service_id, db_session=db_session) - verify_param(service.resource_type, isEqual=True, paramCompare=u'service', httpError=HTTPNotAcceptable, - msgOnFail="Invalid `root_service` specified for resource permission retrieval") + verify_param(service.resource_type, isEqual=True, httpError=HTTPNotAcceptable, + paramName=u'resource_type', paramCompare=u'service', + msgOnFail=UserResourcePermissions_GET_NotAcceptableRootServiceResponseSchema.description) service_obj = service_type_dict[service.type] - verify_param(resource.resource_type, isIn=True, paramCompare=service_obj.resource_types, - httpError=HTTPNotAcceptable, - msgOnFail="Invalid `resource_type` for corresponding service resource permission retrieval") + verify_param(resource.resource_type, isIn=True, httpError=HTTPNotAcceptable, + paramName=u'resource_type', paramCompare=service_obj.resource_types, + msgOnFail=UserResourcePermissions_GET_NotAcceptableResourceTypeResponseSchema.description) return service_obj.resource_types_permissions[resource.resource_type] @@ -119,15 +127,15 @@ def get_resource_root_service(resource, db_session): def create_resource(resource_name, resource_type, parent_id, db_session): - verify_param(resource_name, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `resource_name` '" + str(resource_name) + "' specified for child resource creation") - verify_param(resource_type, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `resource_type` '" + str(resource_type) + "' specified for child resource creation") - verify_param(parent_id, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `parent_id` '" + str(parent_id) + "' specified for child resource creation") + verify_param(resource_name, paramName=u'resource_name', notNone=True, notEmpty=True, httpError=HTTPBadRequest, + msgOnFail="Invalid `resource_name` specified for child resource creation.") + verify_param(resource_type, paramName=u'resource_type', notNone=True, notEmpty=True, httpError=HTTPBadRequest, + msgOnFail="Invalid `resource_type` specified for child resource creation.") + verify_param(parent_id, paramName=u'parent_id', notNone=True, notEmpty=True, httpError=HTTPBadRequest, + msgOnFail="Invalid `parent_id` specified for child resource creation.") parent_resource = evaluate_call(lambda: ResourceService.by_resource_id(parent_id, db_session=db_session), fallback=lambda: db_session.rollback(), httpError=HTTPNotFound, - msgOnFail="Could not find specified resource parent id", + msgOnFail=Resources_POST_NotFoundResponseSchema.description, content={u'parent_id': str(parent_id), u'resource_name': str(resource_name), u'resource_type': str(resource_type)}) @@ -142,8 +150,8 @@ def create_resource(resource_name, resource_type, parent_id, db_session): tree_struct = resource_tree_service.from_parent_deeper(parent_id, limit_depth=1, db_session=db_session) tree_struct_dict = resource_tree_service.build_subtree_strut(tree_struct) direct_children = tree_struct_dict[u'children'] - verify_param(resource_name, notIn=True, httpError=HTTPConflict, - msgOnFail="Resource name already exists at requested tree level for creation", + verify_param(resource_name, paramName=u'resource_name', notIn=True, httpError=HTTPConflict, + msgOnFail=Resources_POST_ConflictResponseSchema.description, paramCompare=[child_dict[u'node'].resource_name for child_dict in direct_children.values()]) def add_resource_in_tree(new_res, db): @@ -153,6 +161,27 @@ def add_resource_in_tree(new_res, db): evaluate_call(lambda: add_resource_in_tree(new_resource, db_session), fallback=lambda: db_session.rollback(), - httpError=HTTPForbidden, msgOnFail="Failed to insert new resource in service tree using parent id") - return valid_http(httpSuccess=HTTPCreated, detail="Create resource successful", - content=format_resource(new_resource, basic_info=True)) + httpError=HTTPForbidden, msgOnFail=Resources_POST_ForbiddenResponseSchema.description) + return valid_http(httpSuccess=HTTPCreated, detail=Resources_POST_OkResponseSchema.description, + content={u'resource': format_resource(new_resource, basic_info=True)}) + + +def delete_resource(request): + resource = get_resource_matchdict_checked(request) + service_push = str2bool(get_multiformat_post(request, 'service_push')) + res_content = {u'resource': format_resource(resource, basic_info=True)} + evaluate_call(lambda: resource_tree_service.delete_branch(resource_id=resource.resource_id, db_session=request.db), + fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, + msgOnFail="Delete resource branch from tree service failed.", content=res_content) + + def remove_service_magpie_and_phoenix(res, svc_push, db): + if res.resource_type != 'service': + svc_push = False + db.delete(res) + if svc_push: + sync_services_phoenix(db.query(models.Service)) + + evaluate_call(lambda: remove_service_magpie_and_phoenix(resource, service_push, request.db), + fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, + msgOnFail=Resource_DELETE_ForbiddenResponseSchema.description, content=res_content) + return valid_http(httpSuccess=HTTPOk, detail=Resource_DELETE_OkResponseSchema.description) diff --git a/magpie/api/management/resource/resource_views.py b/magpie/api/management/resource/resource_views.py index c6e3c6a1f..a6362eb69 100644 --- a/magpie/api/management/resource/resource_views.py +++ b/magpie/api/management/resource/resource_views.py @@ -1,16 +1,18 @@ -from api.api_requests import * -from api.management.service.service_utils import get_services_by_type -from api.management.service.service_formats import format_service_resources -from api.management.resource.resource_utils import * -from api.management.resource.resource_formats import * -from definitions.pyramid_definitions import view_config +from magpie.api.api_requests import * +from magpie.api.management.service.service_utils import get_services_by_type +from magpie.api.management.service.service_formats import format_service_resources +from magpie.api.management.resource.resource_utils import * +from magpie.api.management.resource.resource_formats import * +from magpie.definitions.pyramid_definitions import view_config from common import str2bool from register import sync_services_phoenix from services import service_type_dict -@view_config(route_name='resources', request_method='GET') +@ResourcesAPI.get(tags=[ResourcesTag], response_schemas=Resources_GET_responses) +@view_config(route_name=ResourcesAPI.name, request_method='GET') def get_resources_view(request): + """List all registered resources.""" res_json = {} for svc_type in service_type_dict.keys(): services = get_services_by_type(svc_type, db_session=request.db) @@ -18,52 +20,45 @@ def get_resources_view(request): for svc in services: res_json[svc_type][svc.resource_name] = format_service_resources(svc, request.db, display_all=True) res_json = {u'resources': res_json} - return valid_http(httpSuccess=HTTPOk, detail="Get resources successful", content=res_json) + return valid_http(httpSuccess=HTTPOk, detail=Resources_GET_OkResponseSchema.description, content=res_json) -@view_config(route_name='resource', request_method='GET') +@ResourceAPI.get(tags=[ResourcesTag], response_schemas=Resource_GET_responses) +@view_config(route_name=ResourceAPI.name, request_method='GET') def get_resource_view(request): + """Get resource information.""" resource = get_resource_matchdict_checked(request) res_json = evaluate_call(lambda: format_resource_with_children(resource, db_session=request.db), fallback=lambda: request.db.rollback(), httpError=HTTPInternalServerError, - msgOnFail="Failed building resource children json formatted tree", - content=format_resource(resource, basic_info=True)) - return valid_http(httpSuccess=HTTPOk, detail="Get resource successful", content={resource.resource_id: res_json}) + msgOnFail=Resource_GET_InternalServerErrorResponseSchema.description, + content={u'resource': format_resource(resource, basic_info=True)}) + return valid_http(httpSuccess=HTTPOk, detail=Resource_GET_OkResponseSchema.description, + content={resource.resource_id: res_json}) -@view_config(route_name='resources', request_method='POST') +@ResourcesAPI.post(schema=Resources_POST_RequestBodySchema, tags=[ResourcesTag], + response_schemas=Resources_POST_responses) +@view_config(route_name=ResourcesAPI.name, request_method='POST') def create_resource_view(request): + """Register a new resource.""" resource_name = get_value_multiformat_post_checked(request, 'resource_name') resource_type = get_value_multiformat_post_checked(request, 'resource_type') parent_id = get_value_multiformat_post_checked(request, 'parent_id') return create_resource(resource_name, resource_type, parent_id, request.db) -@view_config(route_name='service_resource', request_method='DELETE') -@view_config(route_name='resource', request_method='DELETE') -def delete_resources(request): - resource = get_resource_matchdict_checked(request) - service_push = str2bool(get_multiformat_post(request, 'service_push')) - res_content = format_resource(resource, basic_info=True) - evaluate_call(lambda: resource_tree_service.delete_branch(resource_id=resource.resource_id, db_session=request.db), - fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, - msgOnFail="Delete resource branch from tree service failed", content=res_content) +@ResourceAPI.delete(schema=Resource_DELETE_RequestSchema(), tags=[ResourcesTag], + response_schemas=Resources_DELETE_responses) +@view_config(route_name=ResourceAPI.name, request_method='DELETE') +def delete_resource_view(request): + """Unregister a resource.""" + return delete_resource(request) - def remove_service_magpie_and_phoenix(res, svc_push, db): - if res.resource_type != 'service': - svc_push = False - db.delete(res) - if svc_push: - sync_services_phoenix(db.query(models.Service)) - - evaluate_call(lambda: remove_service_magpie_and_phoenix(resource, service_push, request.db), - fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, - msgOnFail="Delete resource from db failed", content=res_content) - return valid_http(httpSuccess=HTTPOk, detail="Delete resource successful") - -@view_config(route_name='resource', request_method='PUT') +@ResourceAPI.put(schema=Resource_PUT_RequestSchema(), tags=[ResourcesTag], response_schemas=Resource_PUT_responses) +@view_config(route_name=ResourceAPI.name, request_method='PUT') def update_resource(request): + """Update a resource information.""" resource = get_resource_matchdict_checked(request, 'resource_id') service_push = str2bool(get_multiformat_post(request, 'service_push')) res_old_name = resource.resource_name @@ -77,21 +72,23 @@ def rename_service_magpie_and_phoenix(res, new_name, svc_push, db): sync_services_phoenix(db.query(models.Service)) evaluate_call(lambda: rename_service_magpie_and_phoenix(resource, res_new_name, service_push, request.db), - fallback=lambda: request.db.rollback(), - msgOnFail="Failed to update resource with new name", + fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, + msgOnFail=Resource_PUT_ForbiddenResponseSchema.description, content={u'resource_id': resource.resource_id, u'resource_name': resource.resource_name, u'old_resource_name': res_old_name, u'new_resource_name': res_new_name}) - return valid_http(httpSuccess=HTTPOk, detail="Update resource successful", + return valid_http(httpSuccess=HTTPOk, detail=Resource_PUT_OkResponseSchema.description, content={u'resource_id': resource.resource_id, u'resource_name': resource.resource_name, u'old_resource_name': res_old_name, u'new_resource_name': res_new_name}) -@view_config(route_name='resource_permissions', request_method='GET') +@ResourcePermissionsAPI.get(tags=[ResourcesTag], response_schemas=ResourcePermissions_GET_responses) +@view_config(route_name=ResourcePermissionsAPI.name, request_method='GET') def get_resource_permissions_view(request): + """List all applicable permissions for a resource.""" resource = get_resource_matchdict_checked(request, 'resource_id') res_perm = evaluate_call(lambda: get_resource_permissions(resource, db_session=request.db), fallback=lambda: request.db.rollback(), httpError=HTTPNotAcceptable, - msgOnFail="Invalid resource type to extract permissions", - content=format_resource(resource, basic_info=True)) - return valid_http(httpSuccess=HTTPOk, detail="Get resource permissions successful", - content={u'permission_names': res_perm}) + msgOnFail=ResourcePermissions_GET_NotAcceptableResponseSchema.description, + content={u'resource': format_resource(resource, basic_info=True)}) + return valid_http(httpSuccess=HTTPOk, detail=ResourcePermissions_GET_OkResponseSchema.description, + content={u'permission_names': sorted(res_perm)}) diff --git a/magpie/api/management/service/__init__.py b/magpie/api/management/service/__init__.py index 625aed110..e459959b7 100644 --- a/magpie/api/management/service/__init__.py +++ b/magpie/api/management/service/__init__.py @@ -1,3 +1,4 @@ +from magpie.api.api_rest_schemas import * import logging logger = logging.getLogger(__name__) @@ -6,12 +7,12 @@ def includeme(config): logger.info('Adding api service ...') # Add all the rest api routes - config.add_route('services', '/services') - config.add_route('service', '/services/{service_name}') - config.add_route('services_type', '/services/types/{service_type}') - config.add_route('service_permissions', '/services/{service_name}/permissions') - config.add_route('service_resources', '/services/{service_name}/resources') - config.add_route('service_resource', '/services/{service_name}/resources/{resource_id}') - config.add_route('service_type_resource_types', '/services/types/{service_type}/resources/types') + config.add_route(**service_api_route_info(ServicesAPI)) + config.add_route(**service_api_route_info(ServiceAPI)) + config.add_route(**service_api_route_info(ServiceTypesAPI)) + config.add_route(**service_api_route_info(ServicePermissionsAPI)) + config.add_route(**service_api_route_info(ServiceResourcesAPI)) + config.add_route(**service_api_route_info(ServiceResourceAPI)) + config.add_route(**service_api_route_info(ServiceResourceTypesAPI)) config.scan() diff --git a/magpie/api/management/service/service_formats.py b/magpie/api/management/service/service_formats.py index 62f743954..015fa2a6d 100644 --- a/magpie/api/management/service/service_formats.py +++ b/magpie/api/management/service/service_formats.py @@ -1,9 +1,9 @@ from register import get_twitcher_protected_service_url from services import service_type_dict -from definitions.pyramid_definitions import * -from api.api_except import evaluate_call -from api.management.resource.resource_utils import crop_tree_with_permission -from api.management.resource.resource_formats import get_resource_children, format_resource_tree +from magpie.definitions.pyramid_definitions import * +from magpie.api.api_except import evaluate_call +from magpie.api.management.resource.resource_utils import crop_tree_with_permission +from magpie.api.management.resource.resource_formats import get_resource_children, format_resource_tree def format_service(service, permissions=None): @@ -14,13 +14,13 @@ def fmt_svc(svc, perms): u'service_name': str(svc.resource_name), u'service_type': str(svc.type), u'resource_id': svc.resource_id, - u'permission_names': service_type_dict[svc.type].permission_names if perms is None else perms + u'permission_names': sorted(service_type_dict[svc.type].permission_names if perms is None else perms) } return evaluate_call( lambda: fmt_svc(service, permissions), httpError=HTTPInternalServerError, - msgOnFail="Failed to format service", + msgOnFail="Failed to format service.", content={u'service': repr(service), u'permissions': repr(permissions)} ) diff --git a/magpie/api/management/service/service_utils.py b/magpie/api/management/service/service_utils.py index dfcde4179..1d7617748 100644 --- a/magpie/api/management/service/service_utils.py +++ b/magpie/api/management/service/service_utils.py @@ -1,9 +1,9 @@ from magpie import models from register import SERVICES_PHOENIX_ALLOWED -from definitions.ziggurat_definitions import * +from magpie.definitions.ziggurat_definitions import * from services import service_type_dict -from api.api_except import * -from api.management.group.group_utils import create_group_resource_permission +from magpie.api.api_except import * +from magpie.api.management.group.group_utils import create_group_resource_permission import os diff --git a/magpie/api/management/service/service_views.py b/magpie/api/management/service/service_views.py index f65a01c7a..5f16c7a50 100644 --- a/magpie/api/management/service/service_views.py +++ b/magpie/api/management/service/service_views.py @@ -1,24 +1,37 @@ -from api.management.resource.resource_utils import create_resource -from api.management.service.service_formats import * -from api.management.service.service_utils import * -from api.api_requests import * -from definitions.pyramid_definitions import view_config +from magpie.api.management.resource.resource_utils import create_resource, delete_resource +from magpie.api.management.service.service_formats import * +from magpie.api.management.service.service_utils import * +from magpie.api.api_requests import * +from magpie.api.api_rest_schemas import * +from magpie.definitions.pyramid_definitions import view_config from common import str2bool from register import sync_services_phoenix from models import resource_tree_service from services import service_type_dict -@view_config(route_name='services_type', request_method='GET') -@view_config(route_name='services', request_method='GET') +@ServiceTypesAPI.get(tags=[ServicesTag], response_schemas=ServiceTypes_GET_responses) +@view_config(route_name=ServiceTypesAPI.name, request_method='GET') +def get_services_by_type_view(request): + """List all registered services from a specific type.""" + return get_services_runner(request) + + +@ServicesAPI.get(tags=[ServicesTag], response_schemas=Services_GET_responses) +@view_config(route_name=ServicesAPI.name, request_method='GET') def get_services_view(request): + """List all registered services.""" + return get_services_runner(request) + + +def get_services_runner(request): service_type_filter = request.matchdict.get('service_type') # no check because None/empty is for 'all services' json_response = {} if not service_type_filter: service_types = service_type_dict.keys() else: verify_param(service_type_filter, paramCompare=service_type_dict.keys(), isIn=True, httpError=HTTPNotAcceptable, - msgOnFail="Invalid `service_type` value does not correspond to any of the existing service types", + msgOnFail=Services_GET_NotAcceptableResponseSchema.description, content={u'service_type': str(service_type_filter)}, contentType='application/json') service_types = [service_type_filter] @@ -28,27 +41,32 @@ def get_services_view(request): for service in services: json_response[service_type][service.resource_name] = format_service(service) - return valid_http(httpSuccess=HTTPOk, detail="Get services successful", content={u'services': json_response}) + return valid_http(httpSuccess=HTTPOk, detail=Services_GET_OkResponseSchema.description, + content={u'services': json_response}) -@view_config(route_name='services', request_method='POST') +@ServicesAPI.post(schema=Services_POST_RequestBodySchema(), tags=[ServicesTag], + response_schemas=Services_POST_responses) +@view_config(route_name=ServicesAPI.name, request_method='POST') def register_service(request): + """Registers a new service.""" service_name = get_value_multiformat_post_checked(request, 'service_name') service_url = get_value_multiformat_post_checked(request, 'service_url') service_type = get_value_multiformat_post_checked(request, 'service_type') service_push = str2bool(get_multiformat_post(request, 'service_push')) verify_param(service_type, isIn=True, paramCompare=service_type_dict.keys(), httpError=HTTPBadRequest, - msgOnFail="Specified `service_type` value does not correspond to any of the available types.") + msgOnFail=Services_POST_BadRequestResponseSchema.description) if models.Service.by_service_name(service_name, db_session=request.db): verify_param(service_name, notIn=True, httpError=HTTPConflict, paramCompare=[models.Service.by_service_name(service_name, db_session=request.db).resource_name], - msgOnFail="Specified `service_name` value '" + str(service_name) + "' already exists.") + msgOnFail=Services_POST_ConflictResponseSchema.description, + content={u'service_name': str(service_name)}) service = evaluate_call(lambda: models.Service(resource_name=str(service_name), resource_type=u'service', url=str(service_url), type=str(service_type)), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, - msgOnFail="Service creation for registration failed", + msgOnFail="Service creation for registration failed.", content={u'service_name': str(service_name), u'resource_type': u'service', u'service_url': str(service_url), u'service_type': str(service_type)}) @@ -59,15 +77,17 @@ def add_service_magpie_and_phoenix(svc, svc_push, db): evaluate_call(lambda: add_service_magpie_and_phoenix(service, service_push, request.db), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, - msgOnFail="Service registration forbidden by db", content=format_service(service)) - return valid_http(httpSuccess=HTTPCreated, detail="Service registration to db successful", - content=format_service(service)) + msgOnFail=Services_POST_ForbiddenResponseSchema.description, content=format_service(service)) + return valid_http(httpSuccess=HTTPCreated, detail=Services_POST_CreatedResponseSchema.description, + content={u'service': format_service(service)}) -@view_config(route_name='service', request_method='PUT') +@ServiceAPI.put(schema=Service_PUT_RequestBodySchema(), tags=[ServicesTag], response_schemas=Service_PUT_responses) +@view_config(route_name=ServiceAPI.name, request_method='PUT') def update_service(request): + """Update a service information.""" service = get_service_matchdict_checked(request) - service_push = str2bool(get_multiformat_post(request, 'service_push')) + service_push = str2bool(get_multiformat_post(request, 'service_push', default=False)) def select_update(new_value, old_value): return new_value if new_value is not None and not new_value == '' else old_value @@ -76,7 +96,7 @@ def select_update(new_value, old_value): svc_name = select_update(get_multiformat_post(request, 'service_name'), service.resource_name) svc_url = select_update(get_multiformat_post(request, 'service_url'), service.url) verify_param(svc_name == service.resource_name and svc_url == service.url, notEqual=True, paramCompare=True, - httpError=HTTPBadRequest, msgOnFail="Current service values are already equal to update values") + httpError=HTTPBadRequest, msgOnFail=Service_PUT_BadRequestResponseSchema.description) if svc_name != service.resource_name: all_svc_names = list() @@ -84,7 +104,8 @@ def select_update(new_value, old_value): for svc in get_services_by_type(svc_type, db_session=request.db): all_svc_names.extend(svc.resource_name) verify_param(svc_name, notIn=True, paramCompare=all_svc_names, httpError=HTTPConflict, - msgOnFail="Specified `service_name` value '" + str(svc_name) + "' already exists") + msgOnFail=Service_PUT_ConflictResponseSchema.description, + content={u'service_name': str(svc_name)}) def update_service_magpie_and_phoenix(svc, new_name, new_url, svc_push, db_session): svc.resource_name = new_name @@ -99,26 +120,31 @@ def update_service_magpie_and_phoenix(svc, new_name, new_url, svc_push, db_sessi err_svc_content = {u'service': old_svc_content, u'new_service_name': svc_name, u'new_service_url': svc_url} evaluate_call(lambda: update_service_magpie_and_phoenix(service, svc_name, svc_url, service_push, request.db), fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="Update service failed during value assignment", + httpError=HTTPForbidden, msgOnFail=Service_PUT_ForbiddenResponseSchema.description, content=err_svc_content) - return valid_http(httpSuccess=HTTPOk, detail="Update service successful", content=format_service(service)) + return valid_http(httpSuccess=HTTPOk, detail=Service_PUT_OkResponseSchema.description, + content={u'service': format_service(service)}) -@view_config(route_name='service', request_method='GET') +@ServiceAPI.get(tags=[ServicesTag], response_schemas=Service_GET_responses) +@view_config(route_name=ServiceAPI.name, request_method='GET') def get_service(request): + """Get a service information.""" service = get_service_matchdict_checked(request) - return valid_http(httpSuccess=HTTPOk, detail="Get service successful", - content={str(service.resource_name): format_service(service)}) + return valid_http(httpSuccess=HTTPOk, detail=Service_GET_OkResponseSchema.name, + content={service.resource_name: format_service(service)}) -@view_config(route_name='service', request_method='DELETE') +@ServiceAPI.delete(schema=Service_DELETE_RequestSchema(), tags=[ServicesTag], response_schemas=Service_DELETE_responses) +@view_config(route_name=ServiceAPI.name, request_method='DELETE') def unregister_service(request): + """Unregister a service.""" service = get_service_matchdict_checked(request) - service_push = str2bool(get_multiformat_delete(request, 'service_push')) + service_push = str2bool(get_multiformat_delete(request, 'service_push', default=False)) svc_content = format_service(service) evaluate_call(lambda: resource_tree_service.delete_branch(resource_id=service.resource_id, db_session=request.db), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, - msgOnFail="Delete service from resource tree failed", content=svc_content) + msgOnFail="Delete service from resource tree failed.", content=svc_content) def remove_service_magpie_and_phoenix(svc, svc_push, db_session): db_session.delete(svc) @@ -127,31 +153,46 @@ def remove_service_magpie_and_phoenix(svc, svc_push, db_session): evaluate_call(lambda: remove_service_magpie_and_phoenix(service, service_push, request.db), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, - msgOnFail="Delete service from db failed", content=svc_content) - return valid_http(httpSuccess=HTTPOk, detail="Delete service successful") + msgOnFail=Service_DELETE_ForbiddenResponseSchema.description, content=svc_content) + return valid_http(httpSuccess=HTTPOk, detail=Service_DELETE_OkResponseSchema.description) -@view_config(route_name='service_permissions', request_method='GET') +@ServicePermissionsAPI.get(tags=[ServicesTag], response_schemas=ServicePermissions_GET_responses) +@view_config(route_name=ServicePermissionsAPI.name, request_method='GET') def get_service_permissions(request): + """List all applicable permissions for a service.""" service = get_service_matchdict_checked(request) svc_content = format_service(service) - svc_perms = evaluate_call(lambda: service_type_dict[service.type].permission_names, fallback=request.db.rollback(), - httpError=HTTPNotAcceptable, msgOnFail="Invalid service type specified by service", - content=svc_content) - return valid_http(httpSuccess=HTTPOk, detail="Get service permissions successful", - content={u'permission_names': svc_perms}) + svc_perms = evaluate_call(lambda: service_type_dict[service.type].permission_names, + fallback=request.db.rollback(), httpError=HTTPNotAcceptable, content=svc_content, + msgOnFail=ServicePermissions_GET_NotAcceptableResponseSchema.description) + return valid_http(httpSuccess=HTTPOk, detail=ServicePermissions_GET_OkResponseSchema.description, + content={u'permission_names': sorted(svc_perms)}) + + +@ServiceResourceAPI.delete(schema=ServiceResource_DELETE_RequestSchema(), tags=[ServicesTag], + response_schemas=ServiceResource_DELETE_responses) +@view_config(route_name=ServiceResourceAPI.name, request_method='DELETE') +def delete_service_resource_view(request): + """Unregister a resource.""" + return delete_resource(request) -@view_config(route_name='service_resources', request_method='GET') +@ServiceResourcesAPI.get(tags=[ServicesTag], response_schemas=ServiceResources_GET_responses) +@view_config(route_name=ServiceResourcesAPI.name, request_method='GET') def get_service_resources_view(request): + """List all resources registered under a service.""" service = get_service_matchdict_checked(request) svc_res_json = format_service_resources(service, db_session=request.db, display_all=True) - return valid_http(httpSuccess=HTTPOk, detail="Get service resources successful", + return valid_http(httpSuccess=HTTPOk, detail=ServiceResources_GET_OkResponseSchema.description, content={str(service.resource_name): svc_res_json}) -@view_config(route_name='service_resources', request_method='POST') +@ServiceResourcesAPI.post(schema=ServiceResources_POST_RequestBodySchema, tags=[ServicesTag], + response_schemas=ServiceResources_POST_responses) +@view_config(route_name=ServiceResourcesAPI.name, request_method='POST') def create_service_direct_resource(request): + """Register a new resource directly under a service.""" service = get_service_matchdict_checked(request) resource_name = get_multiformat_post(request, 'resource_name') resource_type = get_multiformat_post(request, 'resource_type') @@ -161,13 +202,15 @@ def create_service_direct_resource(request): return create_resource(resource_name, resource_type, parent_id=parent_id, db_session=request.db) -@view_config(route_name='service_type_resource_types', request_method='GET') +@ServiceResourceTypesAPI.get(tags=[ServicesTag], response_schemas=ServiceResource_GET_responses) +@view_config(route_name=ServiceResourceTypesAPI.name, request_method='GET') def get_service_type_resource_types(request): + """List all resources under a specific service type.""" service_type = get_value_matchdict_checked(request, 'service_type') verify_param(service_type, paramCompare=service_type_dict.keys(), isIn=True, httpError=HTTPNotFound, - msgOnFail="Invalid `service_type` does not exist to obtain its resource types") + msgOnFail=ServiceResourceTypes_GET_NotFoundResponseSchema.description) resource_types = evaluate_call(lambda: service_type_dict[service_type].resource_types, httpError=HTTPForbidden, content={u'service_type': str(service_type)}, - msgOnFail="Failed to obtain resource types for specified service type") - return valid_http(httpSuccess=HTTPOk, detail="Get service type resource types successful", + msgOnFail=ServiceResourceTypes_GET_ForbiddenResponseSchema.description) + return valid_http(httpSuccess=HTTPOk, detail=ServiceResourceTypes_GET_OkResponseSchema.description, content={u'resource_types': resource_types}) diff --git a/magpie/api/management/user/__init__.py b/magpie/api/management/user/__init__.py index 1b3bcf702..00a999174 100644 --- a/magpie/api/management/user/__init__.py +++ b/magpie/api/management/user/__init__.py @@ -1,3 +1,4 @@ +from magpie.api.api_rest_schemas import * import logging logger = logging.getLogger(__name__) @@ -6,22 +7,39 @@ def includeme(config): logger.info('Adding api user ...') # Add all the rest api routes - config.add_route('users', '/users') - config.add_route('user', '/users/{user_name}') - config.add_route('user_groups', 'users/{user_name}/groups') - config.add_route('user_group', '/users/{user_name}/groups/{group_name}') - config.add_route('user_services', '/users/{user_name}/services') - config.add_route('user_inherited_services', '/users/{user_name}/inherited_services') - config.add_route('user_service_permissions', '/users/{user_name}/services/{service_name}/permissions') - config.add_route('user_service_permission', '/users/{user_name}/services/{service_name}/permissions/{permission_name}') - config.add_route('user_service_inherited_permissions', '/users/{user_name}/services/{service_name}/inherited_permissions') - config.add_route('user_service_resources', '/users/{user_name}/services/{service_name}/resources') - config.add_route('user_service_inherited_resources', '/users/{user_name}/services/{service_name}/inherited_resources') - config.add_route('user_resources', '/users/{user_name}/resources') - config.add_route('user_inherited_resources', '/users/{user_name}/inherited_resources') - config.add_route('user_resources_type', '/users/{user_name}/resources/types/{resource_type}') - config.add_route('user_resource_permissions', '/users/{user_name}/resources/{resource_id}/permissions') - config.add_route('user_resource_permission', '/users/{user_name}/resources/{resource_id}/permissions/{permission_name}') - config.add_route('user_resource_inherited_permissions', '/users/{user_name}/resource/{resource_id}/inherited_permissions') + config.add_route(**service_api_route_info(UsersAPI)) + config.add_route(**service_api_route_info(UserAPI)) + config.add_route(**service_api_route_info(UserGroupsAPI)) + config.add_route(**service_api_route_info(UserGroupAPI)) + config.add_route(**service_api_route_info(UserServicesAPI)) + config.add_route(**service_api_route_info(UserInheritedServicesAPI)) + config.add_route(**service_api_route_info(UserServicePermissionsAPI)) + config.add_route(**service_api_route_info(UserServicePermissionAPI)) + config.add_route(**service_api_route_info(UserServiceInheritedPermissionsAPI)) + config.add_route(**service_api_route_info(UserServiceResourcesAPI)) + config.add_route(**service_api_route_info(UserServiceInheritedResourcesAPI)) + config.add_route(**service_api_route_info(UserResourcesAPI)) + config.add_route(**service_api_route_info(UserInheritedResourcesAPI)) + config.add_route(**service_api_route_info(UserResourceTypesAPI)) + config.add_route(**service_api_route_info(UserResourcePermissionsAPI)) + config.add_route(**service_api_route_info(UserResourcePermissionAPI)) + config.add_route(**service_api_route_info(UserResourceInheritedPermissionsAPI)) + # Logged User routes + config.add_route(**service_api_route_info(LoggedUserAPI)) + #config.add_route(**service_api_route_info(LoggedUserGroupsAPI)) + #config.add_route(**service_api_route_info(LoggedUserGroupAPI)) + #config.add_route(**service_api_route_info(LoggedUserServicesAPI)) + #config.add_route(**service_api_route_info(LoggedUserInheritedServicesAPI)) + #config.add_route(**service_api_route_info(LoggedUserServicePermissionsAPI)) + #config.add_route(**service_api_route_info(LoggedUserServicePermissionAPI)) + #config.add_route(**service_api_route_info(LoggedUserServiceInheritedPermissionsAPI)) + #config.add_route(**service_api_route_info(LoggedUserServiceResourcesAPI)) + #config.add_route(**service_api_route_info(LoggedUserServiceInheritedResourcesAPI)) + #config.add_route(**service_api_route_info(LoggedUserResourcesAPI)) + #config.add_route(**service_api_route_info(LoggedUserInheritedResourcesAPI)) + #config.add_route(**service_api_route_info(LoggedUserResourceTypesAPI)) + #config.add_route(**service_api_route_info(LoggedUserResourcePermissionsAPI)) + #config.add_route(**service_api_route_info(LoggedUserResourcePermissionAPI)) + #config.add_route(**service_api_route_info(LoggedUserResourceInheritedPermissionsAPI)) config.scan() diff --git a/magpie/api/management/user/user_formats.py b/magpie/api/management/user/user_formats.py new file mode 100644 index 000000000..54e1bddef --- /dev/null +++ b/magpie/api/management/user/user_formats.py @@ -0,0 +1,18 @@ +from magpie.definitions.pyramid_definitions import HTTPInternalServerError +from magpie.api.api_except import evaluate_call + + +def format_user(user, group_names=None): + def fmt_usr(usr, grp_names): + return { + u'user_name': str(usr.user_name), + u'email': str(usr.email), + u'group_names': sorted(list(grp_names) if grp_names else [grp.group_name for grp in user.groups]), + } + + return evaluate_call( + lambda: fmt_usr(user, group_names), + httpError=HTTPInternalServerError, + msgOnFail="Failed to format user.", + content={u'service': repr(user)} + ) diff --git a/magpie/api/management/user/user_utils.py b/magpie/api/management/user/user_utils.py index 17abff835..9e44b54e4 100644 --- a/magpie/api/management/user/user_utils.py +++ b/magpie/api/management/user/user_utils.py @@ -1,7 +1,9 @@ from magpie import * -from api.api_except import * -from api.management.resource.resource_utils import check_valid_service_resource_permission -from definitions.ziggurat_definitions import * +from magpie.api.api_except import * +from magpie.api.api_rest_schemas import * +from magpie.api.management.resource.resource_utils import check_valid_service_resource_permission +from magpie.api.management.user.user_formats import * +from magpie.definitions.ziggurat_definitions import * from services import service_type_dict import models @@ -11,14 +13,15 @@ def create_user(user_name, password, email, group_name, db_session): # Check that group already exists group_check = evaluate_call(lambda: GroupService.by_group_name(group_name, db_session=db), - httpError=HTTPForbidden, msgOnFail="Group query was refused by db") - verify_param(group_check, notNone=True, httpError=HTTPNotAcceptable, msgOnFail="Group for new user doesn't exist") + httpError=HTTPForbidden, msgOnFail=UserGroup_GET_ForbiddenResponseSchema.description) + verify_param(group_check, notNone=True, httpError=HTTPNotAcceptable, + msgOnFail=UserGroup_Check_ForbiddenResponseSchema.description) # Check if user already exists user_check = evaluate_call(lambda: UserService.by_user_name(user_name=user_name, db_session=db), - httpError=HTTPForbidden, msgOnFail="User check query was refused by db") + httpError=HTTPForbidden, msgOnFail=User_Check_ForbiddenResponseSchema.description) verify_param(user_check, isNone=True, httpError=HTTPConflict, - msgOnFail="User name matches an already existing user name") + msgOnFail=User_Check_ConflictResponseSchema.description) # Create user with specified name and group to assign user_model = models.User(user_name=user_name, email=email) @@ -26,31 +29,32 @@ def create_user(user_name, password, email, group_name, db_session): user_model.set_password(password) user_model.regenerate_security_code() evaluate_call(lambda: db.add(user_model), fallback=lambda: db.rollback(), - httpError=HTTPForbidden, msgOnFail="Failed to add user to db") + httpError=HTTPForbidden, msgOnFail=Users_POST_ForbiddenResponseSchema.description) # Assign user to default group and own group new_user = evaluate_call(lambda: UserService.by_user_name(user_name, db_session=db), - httpError=HTTPForbidden, msgOnFail="New user query was refused by db") + httpError=HTTPForbidden, msgOnFail=UserNew_POST_ForbiddenResponseSchema.description) group_entry = models.UserGroup(group_id=group_check.id, user_id=new_user.id) evaluate_call(lambda: db.add(group_entry), fallback=lambda: db.rollback(), - httpError=HTTPForbidden, msgOnFail="Failed to add user-group to db") + httpError=HTTPForbidden, msgOnFail=UserGroup_GET_ForbiddenResponseSchema.description) - return valid_http(httpSuccess=HTTPCreated, detail="Add user to db successful") + return valid_http(httpSuccess=HTTPCreated, detail=Users_POST_OkResponseSchema.description, + content={u'user': format_user(new_user, [group_name])}) def create_user_resource_permission(permission_name, resource, user_id, db_session): check_valid_service_resource_permission(permission_name, resource, db_session) resource_id = resource.resource_id new_perm = models.UserResourcePermission(resource_id=resource_id, user_id=user_id) - verify_param(new_perm, notNone=True, httpError=HTTPNotAcceptable, - content={u'resource_id': str(resource_id), u'user_id': str(user_id)}, - msgOnFail="Failed to create permission using specified `resource_id` and `user_id`") + verify_param(new_perm, notNone=True, httpError=HTTPNotAcceptable, paramName=u'permission_name', + content={u'resource_id': resource_id, u'user_id': user_id}, + msgOnFail=UserResourcePermissions_POST_NotAcceptableResponseSchema.description) new_perm.perm_name = permission_name evaluate_call(lambda: db_session.add(new_perm), fallback=lambda: db_session.rollback(), - httpError=HTTPConflict, msgOnFail="Permission already exist on service for user, cannot add to db", + httpError=HTTPConflict, msgOnFail=UserResourcePermissions_POST_ConflictResponseSchema.description, content={u'resource_id': resource_id, u'user_id': user_id, u'permission_name': permission_name}) - return valid_http(httpSuccess=HTTPCreated, detail="Create user resource permission successful", - content={u'resource_id': resource_id}) + return valid_http(httpSuccess=HTTPCreated, detail=UserResourcePermissions_POST_CreatedResponseSchema.description, + content={u'resource_id': resource_id, u'user_id': user_id, u'permission_name': permission_name}) def delete_user_resource_permission(permission_name, resource, user_id, db_session): @@ -58,9 +62,9 @@ def delete_user_resource_permission(permission_name, resource, user_id, db_sessi resource_id = resource.resource_id del_perm = UserResourcePermissionService.get(user_id, resource_id, permission_name, db_session) evaluate_call(lambda: db_session.delete(del_perm), fallback=lambda: db_session.rollback(), - httpError=HTTPNotFound, msgOnFail="Could not find user resource permission to delete from db", + httpError=HTTPNotFound, msgOnFail=UserResourcePermissions_DELETE_NotFoundResponseSchema.description, content={u'resource_id': resource_id, u'user_id': user_id, u'permission_name': permission_name}) - return valid_http(httpSuccess=HTTPOk, detail="Delete user resource permission successful") + return valid_http(httpSuccess=HTTPOk, detail=UserResourcePermissions_DELETE_OkResponseSchema.description) def filter_user_permission(resource_permission_tuple_list, user): @@ -93,7 +97,7 @@ def get_user_service_permissions(user, service, db_session, inherited_permission def get_user_resources_permissions_dict(user, db_session, resource_types=None, resource_ids=None, inherited_permissions=True): verify_param(user, notNone=True, httpError=HTTPNotFound, - msgOnFail="Invalid user specified to obtain resource permissions") + msgOnFail="Invalid user specified to obtain resource permissions.") res_perm_tuple_list = user.resources_with_possible_perms(resource_ids=resource_ids, resource_types=resource_types, db_session=db_session) if not inherited_permissions: @@ -122,24 +126,24 @@ def get_user_service_resources_permissions_dict(user, service, db_session, inher def check_user_info(user_name, email, password, group_name): verify_param(user_name, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `user_name` value specified") + paramName=u'user_name', msgOnFail=Users_CheckInfo_Name_BadRequestResponseSchema.description) verify_param(len(user_name), isIn=True, httpError=HTTPBadRequest, - paramCompare=range(1, 1 + USER_NAME_MAX_LENGTH), + paramName=u'user_name', paramCompare=range(1, 1 + USER_NAME_MAX_LENGTH), msgOnFail="Invalid `user_name` length specified " + - "(>{length} characters)".format(length=USER_NAME_MAX_LENGTH)) + "(>{length} characters).".format(length=USER_NAME_MAX_LENGTH)) verify_param(email, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `email` value specified") + paramName=u'email', msgOnFail=Users_CheckInfo_Email_BadRequestResponseSchema.description) verify_param(password, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `password` value specified") + paramName=u'password', msgOnFail=Users_CheckInfo_Password_BadRequestResponseSchema.description) verify_param(group_name, notNone=True, notEmpty=True, httpError=HTTPBadRequest, - msgOnFail="Invalid `group_name` value specified") + paramName=u'group_name', msgOnFail=Users_CheckInfo_GroupName_BadRequestResponseSchema.description) verify_param(user_name, paramCompare=[LOGGED_USER], notIn=True, httpError=HTTPConflict, - msgOnFail="Invalid `user_name` already logged in") + paramName=u'user_name', msgOnFail=Users_CheckInfo_Login_ConflictResponseSchema.description) def get_user_groups_checked(request, user): - verify_param(user, notNone=True, httpError=HTTPNotFound, msgOnFail="User name not found in db") + verify_param(user, notNone=True, httpError=HTTPNotFound, msgOnFail="User name not found in db.") db = request.db group_names = evaluate_call(lambda: [group.group_name for group in user.groups], fallback=lambda: db.rollback(), - httpError=HTTPInternalServerError, msgOnFail="Failed to obtain groups of user") - return group_names + httpError=HTTPInternalServerError, msgOnFail="Failed to obtain groups of user.") + return sorted(group_names) diff --git a/magpie/api/management/user/user_views.py b/magpie/api/management/user/user_views.py index 422325888..9cd926ded 100644 --- a/magpie/api/management/user/user_views.py +++ b/magpie/api/management/user/user_views.py @@ -1,12 +1,38 @@ -from definitions.pyramid_definitions import * -from definitions.ziggurat_definitions import * -from api.api_requests import * -from api.management.user.user_utils import * -from api.management.service.service_formats import format_service, format_service_resources - - -@view_config(route_name='users', request_method='POST') +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.ziggurat_definitions import * +from magpie.api.api_requests import * +from magpie.api.api_rest_schemas import * +from magpie.api.management.user.user_formats import * +from magpie.api.management.user.user_utils import * +from magpie.api.management.service.service_formats import format_service, format_service_resources + + +@UsersAPI.get(tags=[UsersTag], response_schemas={ + '200': Users_GET_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Users_GET_ForbiddenResponseSchema(), +}) +@view_config(route_name=UsersAPI.name, request_method='GET') +def get_users(request): + """List all registered user names.""" + user_name_list = evaluate_call(lambda: [user.user_name for user in models.User.all(db_session=request.db)], + fallback=lambda: request.db.rollback(), + httpError=HTTPForbidden, msgOnFail=Users_GET_ForbiddenResponseSchema.description) + return valid_http(httpSuccess=HTTPOk, content={u'user_names': sorted(user_name_list)}, + detail=Users_GET_OkResponseSchema.description) + + +@UsersAPI.post(schema=Users_POST_RequestSchema(), tags=[UsersTag], response_schemas={ + '200': Users_POST_OkResponseSchema(), + '400': Users_CheckInfo_Name_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': Users_POST_ForbiddenResponseSchema(), + '406': UserGroup_GET_NotAcceptableResponseSchema(), + '409': Users_CheckInfo_Login_ConflictResponseSchema(), +}) +@view_config(route_name=UsersAPI.name, request_method='POST') def create_user_view(request): + """Create a new user.""" user_name = get_multiformat_post(request, 'user_name') email = get_multiformat_post(request, 'email') password = get_multiformat_post(request, 'password') @@ -15,8 +41,25 @@ def create_user_view(request): return create_user(user_name, password, email, group_name, db_session=request.db) -@view_config(route_name='user', request_method='PUT') +@UsersAPI.put(schema=User_PUT_RequestSchema(), tags=[UsersTag], response_schemas={ + '200': Users_PUT_OkResponseSchema(), + '400': Users_CheckInfo_Name_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': UserGroup_GET_ForbiddenResponseSchema(), + '406': UserGroup_GET_NotAcceptableResponseSchema(), + '409': Users_CheckInfo_Login_ConflictResponseSchema(), +}) +@LoggedUserAPI.put(schema=User_PUT_RequestSchema(), tags=[LoggedUserTag], response_schemas={ + '200': Users_PUT_OkResponseSchema(), + '400': Users_CheckInfo_Name_BadRequestResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': UserGroup_GET_ForbiddenResponseSchema(), + '406': UserGroup_GET_NotAcceptableResponseSchema(), + '409': Users_CheckInfo_Login_ConflictResponseSchema(), +}) +@view_config(route_name=UserAPI.name, request_method='PUT') def update_user_view(request): + """Update user information by user name.""" user = get_user_matchdict_checked(request, user_name_key='user_name') new_user_name = get_multiformat_post(request, 'user_name') new_email = get_multiformat_post(request, 'email') @@ -27,7 +70,7 @@ def update_user_view(request): if user.user_name != new_user_name: evaluate_call(lambda: models.User.by_user_name(new_user_name, db_session=request.db), fallback=lambda: request.db.rollback(), - httpError=HTTPConflict, msgOnFail="New name user already exists") + httpError=HTTPConflict, msgOnFail=User_PUT_ConflictResponseSchema.description) user.user_name = new_user_name if user.email != new_email: user.email = new_email @@ -35,57 +78,97 @@ def update_user_view(request): user.set_password(new_password) user.regenerate_security_code() - return valid_http(httpSuccess=HTTPOk, detail="Update user successful.") - + return valid_http(httpSuccess=HTTPOk, detail=Users_PUT_OkResponseSchema.description) -@view_config(route_name='users', request_method='GET') -def get_users(request): - user_name_list = evaluate_call(lambda: [user.user_name for user in models.User.all(db_session=request.db)], - fallback=lambda: request.db.rollback(), - httpError=HTTPForbidden, msgOnFail="Get users query refused by db") - return valid_http(httpSuccess=HTTPOk, detail="Get users successful", content={u'user_names': user_name_list}) - -@view_config(route_name='user', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, response_schemas={ + '200': User_GET_OkResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +}) +@view_config(route_name=UserAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_view(request): + """Get user information by name.""" user = get_user_matchdict_checked(request) - json_response = {u'user_name': user.user_name, - u'email': user.email, - u'group_names': [group.group_name for group in user.groups]} - return valid_http(httpSuccess=HTTPOk, detail="Get user successful", content=json_response) - - -@view_config(route_name='user', request_method='DELETE') + return valid_http(httpSuccess=HTTPOk, detail=User_GET_OkResponseSchema.description, + content={u'user': format_user(user)}) + + +@LoggedUserAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, response_schemas={ + '200': User_GET_OkResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +}) +@view_config(route_name=LoggedUserAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) +def get_logged_user_view(request): + """Get logged user information.""" + user = get_user(request, LOGGED_USER) + return valid_http(httpSuccess=HTTPOk, detail=User_GET_OkResponseSchema.description, + content={u'user': format_user(user)}) + + +@UserAPI.delete(schema=User_DELETE_RequestSchema(), tags=[UsersTag], response_schemas={ + '200': User_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +}) +@LoggedUserAPI.delete(schema=User_DELETE_RequestSchema(), tags=[LoggedUserTag], response_schemas={ + '200': User_DELETE_OkResponseSchema(), + '401': UnauthorizedResponseSchema(), + '403': User_CheckAnonymous_ForbiddenResponseSchema(), + '404': User_CheckAnonymous_NotFoundResponseSchema(), + '422': UnprocessableEntityResponseSchema(), +}) +@view_config(route_name=UserAPI.name, request_method='DELETE') def delete_user(request): + """Delete a user by name.""" user = get_user_matchdict_checked(request) db = request.db evaluate_call(lambda: db.delete(user), fallback=lambda: db.rollback(), - httpError=HTTPForbidden, msgOnFail="Delete user by name refused by db") - return valid_http(httpSuccess=HTTPOk, detail="Delete user successful") + httpError=HTTPForbidden, msgOnFail=User_DELETE_ForbiddenResponseSchema.description) + return valid_http(httpSuccess=HTTPOk, detail=User_DELETE_OkResponseSchema.description) -@view_config(route_name='user_groups', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserGroupsAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, response_schemas=UserGroups_GET_responses) +@LoggedUserGroupsAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserGroups_GET_responses) +@view_config(route_name=UserGroupsAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_groups(request): + """List all groups a user belongs to.""" user = get_user_matchdict_checked(request) group_names = get_user_groups_checked(request, user) - return valid_http(httpSuccess=HTTPOk, detail="Get user groups successful", content={u'group_names': group_names}) + return valid_http(httpSuccess=HTTPOk, detail=UserGroups_GET_OkResponseSchema.description, + content={u'group_names': group_names}) -@view_config(route_name='user_group', request_method='POST') +@UserGroupsAPI.post(schema=UserGroups_POST_RequestSchema(), tags=[UsersTag], response_schemas=UserGroups_POST_responses) +@LoggedUserGroupsAPI.post(schema=UserGroups_POST_RequestSchema(), tags=[LoggedUserTag], + response_schemas=LoggedUserGroups_POST_responses) +@view_config(route_name=UserGroupsAPI.name, request_method='POST') def assign_user_group(request): + """Assign a user to a group.""" db = request.db user = get_user_matchdict_checked(request) group = get_group_matchdict_checked(request) new_user_group = models.UserGroup(group_id=group.id, user_id=user.id) evaluate_call(lambda: db.add(new_user_group), fallback=lambda: db.rollback(), - httpError=HTTPConflict, msgOnFail="User already belongs to this group", + httpError=HTTPConflict, msgOnFail=UserGroups_POST_ConflictResponseSchema.description, content={u'user_name': user.user_name, u'group_name': group.group_name}) - return valid_http(httpSuccess=HTTPCreated, detail="Create user-group assignation successful") + return valid_http(httpSuccess=HTTPCreated, detail=UserGroups_POST_OkResponseSchema.description) -@view_config(route_name='user_group', request_method='DELETE') +@UserGroupAPI.delete(schema=UserGroup_DELETE_RequestSchema(), tags=[UsersTag], + response_schemas=UserGroup_DELETE_responses) +@LoggedUserGroupAPI.delete(schema=UserGroup_DELETE_RequestSchema(), tags=[LoggedUserTag], + response_schemas=LoggedUserGroup_DELETE_responses) +@view_config(route_name=UserGroupAPI.name, request_method='DELETE') def delete_user_group(request): + """Remove a user from a group.""" db = request.db user = get_user_matchdict_checked(request) group = get_group_matchdict_checked(request) @@ -97,9 +180,9 @@ def del_usr_grp(usr, grp): .delete() evaluate_call(lambda: del_usr_grp(user, group), fallback=lambda: db.rollback(), - httpError=HTTPNotFound, msgOnFail="Invalid user-group combination for delete", + httpError=HTTPNotFound, msgOnFail=UserGroup_DELETE_NotFoundResponseSchema.description, content={u'user_name': user.user_name, u'group_name': group.group_name}) - return valid_http(httpSuccess=HTTPOk, detail="Delete user-group successful") + return valid_http(httpSuccess=HTTPOk, detail=UserGroup_DELETE_OkResponseSchema.description) def get_user_resources_runner(request, inherited_group_resources_permissions=True): @@ -127,18 +210,28 @@ def build_json_user_resource_tree(usr): usr_res_dict = evaluate_call(lambda: build_json_user_resource_tree(user), fallback=lambda: db.rollback(), httpError=HTTPNotFound, - msgOnFail="Failed to populate user resources", + msgOnFail=UserResources_GET_NotFoundResponseSchema.description, content={u'user_name': user.user_name, u'resource_types': [u'service']}) - return valid_http(httpSuccess=HTTPOk, detail="Get user resources successful", content={u'resources': usr_res_dict}) + return valid_http(httpSuccess=HTTPOk, detail=UserResources_GET_OkResponseSchema.description, + content={u'resources': usr_res_dict}) -@view_config(route_name='user_resources', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserResourcesAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, response_schemas=UserResources_GET_responses) +@LoggedUserResourcesAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserResources_GET_responses) +@view_config(route_name=UserResourcesAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_resources_view(request): + """List all resources a user has direct permission on (not including his groups permissions).""" return get_user_resources_runner(request, inherited_group_resources_permissions=False) -@view_config(route_name='user_inherited_resources', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserInheritedResourcesAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserResources_GET_responses) +@LoggedUserInheritedResourcesAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserResources_GET_responses) +@view_config(route_name=UserInheritedResourcesAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_inherited_resources_view(request): + """List all resources a user has permission on with his inherited user and groups permissions.""" return get_user_resources_runner(request, inherited_group_resources_permissions=True) @@ -147,30 +240,51 @@ def get_user_resource_permissions_runner(request, inherited_permissions=True): resource = get_resource_matchdict_checked(request, 'resource_id') perm_names = get_user_resource_permissions(resource=resource, user=user, db_session=request.db, inherited_permissions=inherited_permissions) - return valid_http(httpSuccess=HTTPOk, detail="Get user resource permissions successful", - content={u'permission_names': perm_names}) + return valid_http(httpSuccess=HTTPOk, detail=UserResourcePermissions_GET_OkResponseSchema.description, + content={u'permission_names': sorted(perm_names)}) -@view_config(route_name='user_resource_permissions', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserResourcePermissionsAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserResourcePermissions_GET_responses) +@LoggedUserResourcePermissionsAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserResourcePermissions_GET_responses) +@view_config(route_name=UserResourcePermissionsAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_resource_permissions_view(request): + """List all direct permissions a user has on a specific resource (not including his groups permissions).""" return get_user_resource_permissions_runner(request, inherited_permissions=False) -@view_config(route_name='user_resource_inherited_permissions', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserResourceInheritedPermissionsAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserResourcePermissions_GET_responses) +@LoggedUserResourceInheritedPermissionsAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserResourcePermissions_GET_responses) +@view_config(route_name=UserResourceInheritedPermissionsAPI.name, request_method='GET', + permission=NO_PERMISSION_REQUIRED) def get_user_resource_inherited_permissions_view(request): + """List all permissions a user has on a specific resource with his inherited user and groups permissions.""" return get_user_resource_permissions_runner(request, inherited_permissions=True) -@view_config(route_name='user_resource_permissions', request_method='POST') +@UserResourcePermissionsAPI.post(schema=UserResourcePermissions_POST_RequestSchema(), tags=[UsersTag], + response_schemas=UserResourcePermissions_POST_responses) +@LoggedUserResourcePermissionsAPI.post(schema=UserResourcePermissions_POST_RequestSchema(), tags=[LoggedUserTag], + response_schemas=LoggedUserResourcePermissions_POST_responses) +@view_config(route_name=UserResourcePermissionsAPI.name, request_method='POST') def create_user_resource_permission_view(request): + """Create a permission on specific resource for a user.""" user = get_user_matchdict_checked(request) resource = get_resource_matchdict_checked(request) perm_name = get_permission_multiformat_post_checked(request, resource) return create_user_resource_permission(perm_name, resource, user.id, request.db) -@view_config(route_name='user_resource_permission', request_method='DELETE') +@UserResourcePermissionAPI.delete(schema=UserResourcePermission_DELETE_RequestSchema(), tags=[UsersTag], + response_schemas=UserResourcePermission_DELETE_responses) +@LoggedUserResourcePermissionAPI.delete(schema=UserResourcePermission_DELETE_RequestSchema(), tags=[LoggedUserTag], + response_schemas=LoggedUserResourcePermission_DELETE_responses) +@view_config(route_name=UserResourcePermissionAPI.name, request_method='DELETE') def delete_user_resource_permission_view(request): + """Delete a permission on a resource for a user (not including his groups permissions).""" user = get_user_matchdict_checked(request) resource = get_resource_matchdict_checked(request) perm_name = get_permission_matchdict_checked(request, resource) @@ -189,16 +303,26 @@ def get_user_services_runner(request, inherited_group_services_permissions): svc_json[svc.type] = {} svc_json[svc.type][svc.resource_name] = format_service(svc, perms) - return valid_http(httpSuccess=HTTPOk, detail="Get user services successful", content={u'services': svc_json}) + return valid_http(httpSuccess=HTTPOk, detail=UserServices_GET_OkResponseSchema.description, + content={u'services': svc_json}) -@view_config(route_name='user_services', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserServicesAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, response_schemas=UserServices_GET_responses) +@LoggedUserServicesAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserServices_GET_responses) +@view_config(route_name=UserServicesAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_services_view(request): + """List all services a user has direct permission on (not including his groups permissions).""" return get_user_services_runner(request, inherited_group_services_permissions=False) -@view_config(route_name='user_inherited_services', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserInheritedServicesAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserServices_GET_responses) +@LoggedUserInheritedServicesAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserServices_GET_responses) +@view_config(route_name=UserInheritedServicesAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_inherited_services_view(request): + """List all services a user has permission on with his inherited user and groups permissions.""" return get_user_services_runner(request, inherited_group_services_permissions=True) @@ -208,33 +332,53 @@ def get_user_service_permissions_runner(request, inherited_permissions): perms = evaluate_call(lambda: get_user_service_permissions(service=service, user=user, db_session=request.db, inherited_permissions=inherited_permissions), fallback=lambda: request.db.rollback(), httpError=HTTPNotFound, - msgOnFail="Could not find permissions using specified `service_name` and `user_name`", + msgOnFail=UserServicePermissions_GET_NotFoundResponseSchema.description, content={u'service_name': str(service.resource_name), u'user_name': str(user.user_name)}) - message_inherit = ' inherited ' if inherited_permissions else ' ' - return valid_http(httpSuccess=HTTPOk, detail="Get user service{}permissions successful".format(message_inherit), - content={u'permission_names': perms}) + return valid_http(httpSuccess=HTTPOk, detail=UserServicePermissions_GET_OkResponseSchema.description, + content={u'permission_names': sorted(perms)}) -@view_config(route_name='user_service_permissions', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserServicePermissionsAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserServicePermissions_GET_responses) +@LoggedUserServicePermissionsAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserServicePermissions_GET_responses) +@view_config(route_name=UserServicePermissionsAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_service_permissions_view(request): + """List all direct permissions a user has on a service (not including his groups permissions).""" return get_user_service_permissions_runner(request, inherited_permissions=False) -@view_config(route_name='user_service_inherited_permissions', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserServiceInheritedPermissionsAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserServicePermissions_GET_responses) +@LoggedUserServiceInheritedPermissionsAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserServicePermissions_GET_responses) +@view_config(route_name=UserServiceInheritedPermissionsAPI.name, request_method='GET', + permission=NO_PERMISSION_REQUIRED) def get_user_service_inherited_permissions_view(request): + """List all permissions a user has on a service using all his inherited user and groups permissions.""" return get_user_service_permissions_runner(request, inherited_permissions=True) -@view_config(route_name='user_service_permissions', request_method='POST') +@UserServicePermissionsAPI.post(schema=UserServicePermissions_POST_RequestBodySchema, tags=[UsersTag], + response_schemas=UserServicePermissions_POST_responses) +@LoggedUserServicePermissionsAPI.post(schema=UserServicePermissions_POST_RequestBodySchema, tags=[LoggedUserTag], + response_schemas=LoggedUserServicePermissions_POST_responses) +@view_config(route_name=UserServicePermissionsAPI.name, request_method='POST') def create_user_service_permission(request): + """Create a permission on a service for a user.""" user = get_user_matchdict_checked(request) service = get_service_matchdict_checked(request) perm_name = get_permission_multiformat_post_checked(request, service) return create_user_resource_permission(perm_name, service, user.id, request.db) -@view_config(route_name='user_service_permission', request_method='DELETE') +@UserServicePermissionAPI.delete(schema=UserServicePermission_DELETE_RequestSchema, tags=[UsersTag], + response_schemas=UserServicePermission_DELETE_responses) +@LoggedUserServicePermissionAPI.delete(schema=UserServicePermission_DELETE_RequestSchema, tags=[LoggedUserTag], + response_schemas=LoggedUserServicePermission_DELETE_responses) +@view_config(route_name=UserServicePermissionAPI.name, request_method='DELETE') def delete_user_service_permission(request): + """Delete a permission on a service for a user (not including his groups permissions).""" user = get_user_matchdict_checked(request) service = get_service_matchdict_checked(request) perm_name = get_permission_multiformat_post_checked(request, service) @@ -246,7 +390,7 @@ def get_user_service_resource_permissions_runner(request, inherited_permissions) Resource permissions a user as on a specific service :param request: - :param inherited_permissions: only direct permissions if False, else resolve permissions with user and its groups. + :param inherited_permissions: only direct permissions if False, else resolve permissions with user and his groups. :return: """ user = get_user_matchdict_checked(request) @@ -262,15 +406,26 @@ def get_user_service_resource_permissions_runner(request, inherited_permissions) resources_perms_dict=resources_perms_dict, display_all=False ) - return valid_http(httpSuccess=HTTPOk, detail="Get user service resources successful", + return valid_http(httpSuccess=HTTPOk, detail=UserServiceResources_GET_OkResponseSchema.description, content={u'service': user_svc_res_json}) -@view_config(route_name='user_service_resources', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserServiceResourcesAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserServiceResources_GET_responses) +@LoggedUserServiceResourcesAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserServiceResources_GET_responses) +@view_config(route_name=UserServiceResourcesAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_service_resources_view(request): + """List all resources under a service a user has direct permission on (not including his groups permissions).""" return get_user_service_resource_permissions_runner(request, inherited_permissions=False) -@view_config(route_name='user_service_inherited_resources', request_method='GET', permission=NO_PERMISSION_REQUIRED) +@UserServiceInheritedResourcesAPI.get(tags=[UsersTag], api_security=SecurityEveryoneAPI, + response_schemas=UserServiceResources_GET_responses) +@LoggedUserServiceInheritedResourcesAPI.get(tags=[LoggedUserTag], api_security=SecurityEveryoneAPI, + response_schemas=LoggedUserServiceResources_GET_responses) +@view_config(route_name=UserServiceInheritedResourcesAPI.name, request_method='GET', permission=NO_PERMISSION_REQUIRED) def get_user_service_inherited_resources_view(request): + """List all resources under a service a user has permission on using all his inherited user and groups + permissions.""" return get_user_service_resource_permissions_runner(request, inherited_permissions=True) diff --git a/magpie/common.py b/magpie/common.py index b08876ed2..e0784f5b5 100644 --- a/magpie/common.py +++ b/magpie/common.py @@ -1,3 +1,6 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +from __future__ import unicode_literals from distutils.dir_util import mkpath import logging import os diff --git a/magpie/db.py b/magpie/db.py index 4094b5e38..29b0ffbe2 100644 --- a/magpie/db.py +++ b/magpie/db.py @@ -1,6 +1,9 @@ -from definitions.alembic_definitions import * -from definitions.sqlalchemy_definitions import * -import ConfigParser +from magpie import MAGPIE_ROOT +from magpie.definitions.alembic_definitions import * +from magpie.definitions.sqlalchemy_definitions import * +from common import print_log +# noinspection PyCompatibility +import configparser import transaction import models import inspect @@ -9,7 +12,6 @@ import logging logger = logging.getLogger(__name__) - # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines from models import * @@ -78,20 +80,14 @@ def get_db_session_from_config_ini(config_ini_path, ini_main_section_name='app:m def get_settings_from_config_ini(config_ini_path, ini_main_section_name='app:magpie_app'): - parser = ConfigParser.ConfigParser() + parser = configparser.ConfigParser() parser.read([config_ini_path]) settings = dict(parser.items(ini_main_section_name)) return settings -def get_alembic_ini_path(): - curr_path = os.path.dirname(os.path.abspath(__file__)) - curr_path = os.path.dirname(curr_path) - return '{path}/alembic.ini'.format(path=curr_path) - - def run_database_migration(): - alembic_args = ['-c', get_alembic_ini_path(), 'upgrade', 'heads'] + alembic_args = ['-c', '{path}/alembic.ini'.format(path=MAGPIE_ROOT), 'upgrade', 'heads'] alembic.config.main(argv=alembic_args) diff --git a/magpie/definitions/pyramid_definitions.py b/magpie/definitions/pyramid_definitions.py index 79751e2f9..3871c1085 100644 --- a/magpie/definitions/pyramid_definitions.py +++ b/magpie/definitions/pyramid_definitions.py @@ -17,7 +17,7 @@ HTTPInternalServerError, ) from pyramid.interfaces import IAuthenticationPolicy, IAuthorizationPolicy -from pyramid.response import Response +from pyramid.response import Response, FileResponse from pyramid.view import ( view_config, notfound_view_config, diff --git a/magpie/definitions/sqlalchemy_definitions.py b/magpie/definitions/sqlalchemy_definitions.py index f2db153d8..2f603774d 100644 --- a/magpie/definitions/sqlalchemy_definitions.py +++ b/magpie/definitions/sqlalchemy_definitions.py @@ -6,4 +6,5 @@ from sqlalchemy.orm import relationship, sessionmaker, configure_mappers from sqlalchemy.sql import select from sqlalchemy import engine_from_config, pool, create_engine +from sqlalchemy import exc as sa_exc import sqlalchemy as sa diff --git a/magpie/demo.py b/magpie/demo.py index e8babdbb0..e5e81d558 100644 --- a/magpie/demo.py +++ b/magpie/demo.py @@ -11,7 +11,7 @@ def main(global_settings, **settings): ) config.include('pyramid_mako') - config.include('ui') + config.include('magpie.ui') config.scan() return config.make_wsgi_app() diff --git a/magpie/helpers/register_default_users.py b/magpie/helpers/register_default_users.py index d89d2d2a5..4dcecadf2 100644 --- a/magpie/helpers/register_default_users.py +++ b/magpie/helpers/register_default_users.py @@ -1,6 +1,6 @@ from magpie import * from common import * -from definitions.ziggurat_definitions import * +from magpie.definitions.ziggurat_definitions import * import models import db import transaction @@ -47,9 +47,9 @@ def register_user_with_group(user_name, group_name, email, password, db_session) def init_anonymous(db_session): register_user_with_group(user_name=ANONYMOUS_USER, - group_name=ANONYMOUS_USER, + group_name=ANONYMOUS_GROUP, email=ANONYMOUS_USER + '@mail.com', - password=ANONYMOUS_USER, + password=ANONYMOUS_PASSWORD, db_session=db_session) @@ -87,10 +87,7 @@ def register_default_users(): time.sleep(2) raise_log('Database not ready') - magpie_module_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - magpie_ini_file = '{}/magpie.ini'.format(magpie_module_dir) - db_session = db.get_db_session_from_config_ini(magpie_ini_file) - + db_session = db.get_db_session_from_config_ini(MAGPIE_INI_FILE_PATH) init_admin(db_session) init_anonymous(db_session) init_user_group(db_session) diff --git a/magpie/helpers/register_providers.py b/magpie/helpers/register_providers.py index bad498a3b..431eb64d6 100644 --- a/magpie/helpers/register_providers.py +++ b/magpie/helpers/register_providers.py @@ -1,13 +1,13 @@ +from magpie import MAGPIE_PROVIDERS_CONFIG_PATH, MAGPIE_INI_FILE_PATH from register import magpie_register_services_from_config from db import get_db_session_from_config_ini -import os import argparse if __name__ == "__main__": parser = argparse.ArgumentParser(description="Register service providers into Magpie and Phoenix") parser.add_argument('-c', '--config-file', metavar='config_file', dest='config_file', - type=str, default=os.path.join(os.path.dirname(os.path.abspath(__file__)), "providers.cfg"), + type=str, default=MAGPIE_PROVIDERS_CONFIG_PATH, help="configuration file to employ for services registration (default: %(default)s)") parser.add_argument('-f', '--force-update', default=False, action='store_true', dest='force_update', help="enforce update of services URL if conflicting services are found (default: %(default)s)") @@ -23,8 +23,7 @@ db_session = None if args.use_db_session: - config_ini = '{}/magpie.ini'.format(os.path.abspath(os.path.dirname(os.path.dirname(__file__)))) - db_session = get_db_session_from_config_ini(config_ini) + db_session = get_db_session_from_config_ini(MAGPIE_INI_FILE_PATH) magpie_register_services_from_config(args.config_file, push_to_phoenix=args.phoenix_push, force_update=args.force_update, disable_getcapabilities=args.no_getcapabilities, db_session=db_session) diff --git a/magpie/magpie.ini b/magpie/magpie.ini index b7f29426a..76da9346a 100644 --- a/magpie/magpie.ini +++ b/magpie/magpie.ini @@ -6,8 +6,8 @@ [composite:main] use = egg:Paste#urlmap / = magpie_app -/api = api_app -/magpie/api = api_app +#/api = api_app +#/magpie/api = api_app [app:magpie_app] use = egg:magpie @@ -39,7 +39,7 @@ ziggurat_foundations.session_provider_callable = models:get_session_callable [app:api_app] use = egg:Paste#static -document_root = %(here)s/ui/swagger-ui +document_root = %(here)s/ui/swagger [filter:urlprefix] use = egg:PasteDeploy#prefix diff --git a/magpie/magpie.py b/magpie/magpie.py deleted file mode 100644 index a0deada9d..000000000 --- a/magpie/magpie.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -""" -Magpie is a service for AuthN and AuthZ based on Ziggurat-Foundations -""" - -# -- Standard library -------------------------------------------------------- -import logging.config -import argparse -import os -import time -import logging -LOGGER = logging.getLogger(__name__) - -# -- Definitions -from definitions.alembic_definitions import * -from definitions.pyramid_definitions import * -from definitions.sqlalchemy_definitions import * -from definitions.ziggurat_definitions import * - -# -- Project specific -------------------------------------------------------- -from __init__ import * -from api.api_except import * -from api.api_rest_schemas import * -from common import * -from helpers.register_default_users import register_default_users -from helpers.register_providers import magpie_register_services_from_config -import models -import db -import __meta__ -MAGPIE_MODULE_DIR = os.path.abspath(os.path.dirname(__file__)) -MAGPIE_ROOT = os.path.dirname(MAGPIE_MODULE_DIR) -sys.path.insert(0, MAGPIE_MODULE_DIR) - - -@VersionAPI.get(schema=Version_GET_Schema(), tags=[APITag], response_schemas={ - '200': Version_GET_OkResponseSchema(description="Get version successful.")}) -@view_config(route_name='version', request_method='GET', permission=NO_PERMISSION_REQUIRED) -def get_version(request): - return valid_http(httpSuccess=HTTPOk, - content={u'version': __meta__.__version__, u'db_version': db.get_database_revision(request.db)}, - detail="Get version successful.", contentType='application/json') - - -#@NotFoundAPI.get(schema=NotFoundResponseSchema(), response_schemas={ -# '404': NotFoundResponseSchema(description="Route not found")}) -@notfound_view_config() -def not_found(request): - content = get_request_info(request, default_msg="The route resource could not be found.") - return raise_http(nothrow=True, httpError=HTTPNotFound, contentType='application/json', - detail=content['detail'], content=content) - - -@exception_view_config() -def internal_server_error(request): - content = get_request_info(request, default_msg="Internal Server Error. Unhandled exception occurred.") - return raise_http(nothrow=True, httpError=HTTPInternalServerError, contentType='application/json', - detail=content['detail'], content=content) - - -@forbidden_view_config() -def unauthorized_access(request): - # if not overridden, default is HTTPForbidden [403], which is for a slightly different situation - # this better reflects the HTTPUnauthorized [401] user access with specified AuthZ headers - # [http://www.restapitutorial.com/httpstatuscodes.html] - msg = "Unauthorized. Insufficient user privileges or missing authentication headers." - content = get_request_info(request, default_msg=msg) - return raise_http(nothrow=True, httpError=HTTPUnauthorized, contentType='application/json', - detail=content['detail'], content=content) - - -def get_request_info(request, default_msg="undefined"): - content = {u'route_name': str(request.upath_info), u'request_url': str(request.url), u'detail': default_msg} - if hasattr(request, 'exception'): - if hasattr(request.exception, 'json'): - if type(request.exception.json) is dict: - content.update(request.exception.json) - elif hasattr(request, 'matchdict'): - if request.matchdict is not None and request.matchdict != '': - content.update(request.matchdict) - return content - - -def main(global_config=None, **settings): - """ - This function returns a Pyramid WSGI application. - """ - - settings['magpie.root'] = MAGPIE_ROOT - settings['magpie.module'] = MAGPIE_MODULE_DIR - - # migrate db as required and check if database is ready - print_log('Running database migration (as required) ...') - try: - db.run_database_migration() - except ImportError: - pass - except Exception as e: - raise_log('Database migration failed [{}]'.format(str(e))) - if not db.is_database_ready(): - time.sleep(2) - raise_log('Database not ready') - - settings['magpie.phoenix_push'] = str2bool(os.getenv('PHOENIX_PUSH', False)) - - print_log('Register default providers...', LOGGER) - providers_config_path = '{}/providers.cfg'.format(MAGPIE_ROOT) - magpie_ini_path = '{}/magpie.ini'.format(MAGPIE_MODULE_DIR) - svc_db_session = db.get_db_session_from_config_ini(magpie_ini_path) - magpie_register_services_from_config(providers_config_path, push_to_phoenix=settings['magpie.phoenix_push'], - force_update=True, disable_getcapabilities=False, db_session=svc_db_session) - - print_log('Register default users...') - register_default_users() - - print_log('Running configurations setup...') - magpie_url_template = 'http://{hostname}:{port}/magpie' - port = os.getenv('MAGPIE_PORT') - if port: - settings['magpie.port'] = port - hostname = os.getenv('HOSTNAME') - if hostname: - settings['magpie.url'] = magpie_url_template.format(hostname=hostname, port=settings['magpie.port']) - - magpie_secret = os.getenv('MAGPIE_SECRET') - if magpie_secret is None: - print_log('Use default secret from magpie.ini', level=logging.DEBUG) - magpie_secret = settings['magpie.secret'] - - authn_policy = AuthTktAuthenticationPolicy( - magpie_secret, - callback=groupfinder, - ) - authz_policy = ACLAuthorizationPolicy() - - config = Configurator( - settings=settings, - root_factory=models.RootFactory, - authentication_policy=authn_policy, - authorization_policy=authz_policy - ) - - config.include('magpie') - - # include api views - magpie_api_path = '{}/__api__'.format(settings['magpie.url']) - magpie_api_view = '{}/api-explorer'.format(settings['magpie.url']) - config.cornice_enable_openapi_view( - api_path=magpie_api_path, - title='Magpie REST API', - description="OpenAPI documentation", - version=__meta__.__version__ - ) - config.cornice_enable_openapi_explorer(api_explorer_path=magpie_api_view) - #config.register_swagger_ui(swagger_ui_path=magpie_api_path) - - config.scan('magpie') - config.set_default_permission(ADMIN_PERM) - - wsgi_app = config.make_wsgi_app() - return wsgi_app - - -if __name__ == '__main__': - main() diff --git a/magpie/magpiectl.py b/magpie/magpiectl.py new file mode 100644 index 000000000..bd9c2b466 --- /dev/null +++ b/magpie/magpiectl.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +Magpie is a service for AuthN and AuthZ based on Ziggurat-Foundations +""" + +# -- Standard library --403------------------------------------------------------ +import logging.config +import argparse +import time +import warnings +import logging +LOGGER = logging.getLogger(__name__) + +# -- Definitions +from magpie.definitions.alembic_definitions import * +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.sqlalchemy_definitions import * +from magpie.definitions.ziggurat_definitions import * + +# -- Project specific -------------------------------------------------------- +from __init__ import * +from magpie.api.api_except import * +from magpie.api.api_rest_schemas import * +from magpie.api.api_generic import * +from magpie.common import * +from magpie.helpers.register_default_users import register_default_users +from magpie.helpers.register_providers import magpie_register_services_from_config +from magpie.security import auth_config_from_settings +from magpie import models, db, __meta__ + + +def main(global_config=None, **settings): + """ + This function returns a Pyramid WSGI application. + """ + + settings['magpie.root'] = MAGPIE_ROOT + settings['magpie.module'] = MAGPIE_MODULE_DIR + + # migrate db as required and check if database is ready + if not settings.get('magpie.db_migration_disabled', False): + print_log('Running database migration (as required) ...') + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=sa_exc.SAWarning) + db.run_database_migration() + except ImportError: + pass + except Exception as e: + raise_log('Database migration failed [{}]'.format(str(e))) + if not db.is_database_ready(): + time.sleep(2) + raise_log('Database not ready') + + settings['magpie.phoenix_push'] = str2bool(os.getenv('PHOENIX_PUSH', False)) + + print_log('Register default providers...', LOGGER) + svc_db_session = db.get_db_session_from_config_ini(MAGPIE_INI_FILE_PATH) + magpie_register_services_from_config(MAGPIE_PROVIDERS_CONFIG_PATH, push_to_phoenix=settings['magpie.phoenix_push'], + force_update=True, disable_getcapabilities=False, db_session=svc_db_session) + + print_log('Register default users...') + register_default_users() + + print_log('Running configurations setup...') + magpie_url_template = 'http://{hostname}:{port}/magpie' + port = os.getenv('MAGPIE_PORT') + if port: + settings['magpie.port'] = port + hostname = os.getenv('HOSTNAME') + if hostname: + settings['magpie.url'] = magpie_url_template.format(hostname=hostname, port=settings['magpie.port']) + + # avoid cornice conflicting with magpie exception views + settings['handle_exceptions'] = False + + config = auth_config_from_settings(settings) + config.include('magpie') + # Don't use scan otherwise modules like 'magpie.adapter' are + # automatically found and cause import errors on missing packages + #config.scan('magpie') + config.set_default_permission(ADMIN_PERM) + + # include api views + print_log('Running api documentation setup...') + magpie_api_gen_disabled = os.getenv('MAGPIE_API_GENERATION_DISABLED') + if magpie_api_gen_disabled: + settings['magpie.api_generation_disabled'] = magpie_api_gen_disabled + if 'magpie.api_generation_disabled' not in settings: + settings['magpie.api_generation_disabled'] = False + + if not settings['magpie.api_generation_disabled']: + magpie_api_path = '{base}{path}'.format(base=settings['magpie.url'], path=SwaggerGenerator.path) + config.cornice_enable_openapi_view( + api_path=magpie_api_path, + title=TitleAPI, + description=__meta__.__description__, + version=__meta__.__version__ + ) + config.add_route(**service_api_route_info(SwaggerGenerator)) + config.add_view(api_schema, route_name=SwaggerGenerator.name, request_method='GET', + renderer='json', permission=NO_PERMISSION_REQUIRED) + config.add_route(**service_api_route_info(SwaggerAPI)) + + print_log('Starting Magpie app...') + wsgi_app = config.make_wsgi_app() + return wsgi_app + + +if __name__ == '__main__': + main() diff --git a/magpie/models.py b/magpie/models.py index e522416b0..822858056 100644 --- a/magpie/models.py +++ b/magpie/models.py @@ -1,7 +1,7 @@ -from definitions.pyramid_definitions import * -from definitions.ziggurat_definitions import * -from definitions.sqlalchemy_definitions import * -from api.api_except import * +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.ziggurat_definitions import * +from magpie.definitions.sqlalchemy_definitions import * +from magpie.api.api_except import * Base = declarative_base() diff --git a/magpie/register.py b/magpie/register.py index c9d899091..d204b0526 100644 --- a/magpie/register.py +++ b/magpie/register.py @@ -6,7 +6,6 @@ import transaction import models from services import service_type_dict -from api.management.user.user_utils import create_user_resource_permission from common import * LOGGER = logging.getLogger(__name__) @@ -184,9 +183,9 @@ def get_magpie_url(): return 'http://{0}{1}'.format(hostname, magpie_port) -def get_twitcher_protected_service_url(magpie_service_name): +def get_twitcher_protected_service_url(magpie_service_name, hostname=None): try: - hostname = os.getenv('HOSTNAME') + hostname = hostname or os.getenv('HOSTNAME') twitcher_proxy = os.getenv('TWITCHER_PROTECTED_PATH') if hostname is None: raise ValueError("Environment variable was None", 'HOSTNAME') diff --git a/magpie/security.py b/magpie/security.py index 8f9a661c8..cd29401b4 100644 --- a/magpie/security.py +++ b/magpie/security.py @@ -1,6 +1,6 @@ -from definitions.pyramid_definitions import * -from definitions.ziggurat_definitions import * -from api.esgf import esgfopenid +from magpie.definitions.pyramid_definitions import * +from magpie.definitions.ziggurat_definitions import * +from magpie.api.esgf import esgfopenid from common import print_log from authomatic import Authomatic, provider_id from authomatic.providers import oauth2, openid diff --git a/magpie/services.py b/magpie/services.py index 1f09c731e..72d56fc22 100644 --- a/magpie/services.py +++ b/magpie/services.py @@ -1,9 +1,8 @@ from magpie import * from owsrequest import * -from definitions.ziggurat_definitions import * -from pyramid.security import Everyone as EVERYONE -from pyramid.security import Allow -from api.api_except import * +from magpie.definitions.ziggurat_definitions import * +from magpie.definitions.pyramid_definitions import EVERYONE, ALLOW +from magpie.api.api_except import * import models @@ -170,7 +169,7 @@ def __acl__(self): netcdf_file = netcdf_file.rsplit('/', 1)[0] else: - return [(Allow, EVERYONE, permission_requested,)] + return [(ALLOW, EVERYONE, permission_requested,)] if netcdf_file: verify_param('outputs/', paramCompare=netcdf_file, httpError=HTTPNotFound, diff --git a/magpie/ui/__init__.py b/magpie/ui/__init__.py index a9f490de1..4872d4120 100644 --- a/magpie/ui/__init__.py +++ b/magpie/ui/__init__.py @@ -6,6 +6,8 @@ def includeme(config): logger.info('Adding ui routes ...') # Add all the admin ui routes - config.include('ui.login') - config.include('ui.home') - config.include('ui.management') + config.include('magpie.ui.login') + config.include('magpie.ui.home') + config.include('magpie.ui.management') + config.include('magpie.ui.swagger') + config.scan() diff --git a/magpie/ui/home/__init__.py b/magpie/ui/home/__init__.py index 2878d2a9c..129d52a09 100644 --- a/magpie/ui/home/__init__.py +++ b/magpie/ui/home/__init__.py @@ -1,6 +1,6 @@ import logging import requests -from definitions.pyramid_definitions import * +from magpie.definitions.pyramid_definitions import * logger = logging.getLogger(__name__) diff --git a/magpie/ui/home/templates/home.mako b/magpie/ui/home/templates/home.mako index 10ad49386..598156374 100644 --- a/magpie/ui/home/templates/home.mako +++ b/magpie/ui/home/templates/home.mako @@ -6,15 +6,15 @@ diff --git a/magpie/ui/home/templates/template.mako b/magpie/ui/home/templates/template.mako index 3f59daad1..1c75e5502 100644 --- a/magpie/ui/home/templates/template.mako +++ b/magpie/ui/home/templates/template.mako @@ -4,8 +4,8 @@