def update_service_view(request): """ Update service information. """ service = ar.get_service_matchdict_checked(request) service_push = asbool(ar.get_multiformat_body(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 # None/Empty values are accepted in case of unspecified svc_name = select_update(ar.get_multiformat_body(request, "service_name"), service.resource_name) svc_url = select_update(ar.get_multiformat_body(request, "service_url"), service.url) ax.verify_param(svc_name, param_compare="types", not_equal=True, param_name="service_name", http_error=HTTPForbidden, msg_on_fail=s.Service_PATCH_ForbiddenResponseSchema_ReservedKeyword.description) ax.verify_param(svc_name == service.resource_name and svc_url == service.url, not_equal=True, param_compare=True, param_name="service_name/service_url", http_error=HTTPBadRequest, msg_on_fail=s.Service_PATCH_BadRequestResponseSchema.description) # config explicitly provided as None (null) means override (erase) # to leave it as is, just don't specific the field old_svc_config = service.configuration new_svc_config = ar.get_multiformat_body(request, "configuration") if old_svc_config != new_svc_config: if new_svc_config is not None: ax.verify_param(new_svc_config, param_compare=dict, is_type=True, http_error=HTTPUnprocessableEntity, msg_on_fail=s.Service_CheckConfig_UnprocessableEntityResponseSchema.description) service.configuration = new_svc_config if svc_name != service.resource_name: all_services = request.db.query(models.Service) all_svc_names = [svc.resource_name for svc in all_services] ax.verify_param(svc_name, not_in=True, param_compare=all_svc_names, with_param=False, http_error=HTTPConflict, content={"service_name": str(svc_name)}, msg_on_fail=s.Service_PATCH_ConflictResponseSchema.description) def update_service_magpie_and_phoenix(_svc, new_name, new_url, svc_push, db_session): _svc.resource_name = new_name _svc.url = new_url has_getcap = Permission.GET_CAPABILITIES in SERVICE_TYPE_DICT[_svc.type].permissions if svc_push and _svc.type in SERVICES_PHOENIX_ALLOWED and has_getcap: # (re)apply getcapabilities to updated service to ensure updated push su.add_service_getcapabilities_perms(_svc, db_session) sync_services_phoenix(db_session.query(models.Service)) # push all services old_svc_content = sf.format_service(service, show_private_url=True) err_svc_content = {"service": old_svc_content, "new_service_name": svc_name, "new_service_url": svc_url} ax.evaluate_call(lambda: update_service_magpie_and_phoenix(service, svc_name, svc_url, service_push, request.db), fallback=lambda: request.db.rollback(), http_error=HTTPForbidden, msg_on_fail=s.Service_PATCH_ForbiddenResponseSchema.description, content=err_svc_content) return ax.valid_http(http_success=HTTPOk, detail=s.Service_PATCH_OkResponseSchema.description, content={"service": sf.format_service(service, show_private_url=True)})
def get_services_runner(request): """ Generates services response format from request conditions. Obtains the full or filtered list of services categorized by type, or listed as flat list according to request path and query parameters. """ service_type_filter = request.matchdict.get("service_type") # no check because None/empty is for 'all services' services_as_list = asbool(ar.get_query_param(request, "flatten", False)) if not service_type_filter: service_types = SERVICE_TYPE_DICT.keys() else: ax.verify_param(service_type_filter, param_compare=SERVICE_TYPE_DICT.keys(), is_in=True, http_error=HTTPBadRequest, msg_on_fail=s.Services_GET_BadRequestResponseSchema.description, content={"service_type": str(service_type_filter)}, content_type=CONTENT_TYPE_JSON) service_types = [service_type_filter] svc_content = [] if services_as_list else {} # type: Union[List[JSON], JSON] for service_type in service_types: services = su.get_services_by_type(service_type, db_session=request.db) if not services_as_list: svc_content[service_type] = {} for service in services: svc_fmt = sf.format_service(service, show_private_url=True) if services_as_list: svc_content.append(svc_fmt) # pylint: disable=E1101 else: svc_content[service_type][service.resource_name] = svc_fmt return ax.valid_http(http_success=HTTPOk, content={"services": svc_content}, detail=s.Services_GET_OkResponseSchema.description)
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: ax.verify_param( service_type_filter, paramCompare=SERVICE_TYPE_DICT.keys(), isIn=True, httpError=HTTPBadRequest, msgOnFail=s.Services_GET_BadRequestResponseSchema.description, content={u"service_type": str(service_type_filter)}, contentType=CONTENT_TYPE_JSON) service_types = [service_type_filter] for service_type in service_types: services = su.get_services_by_type(service_type, db_session=request.db) json_response[service_type] = {} for service in services: json_response[service_type][ service.resource_name] = sf.format_service( service, show_private_url=True) return ax.valid_http(httpSuccess=HTTPOk, content={u"services": json_response}, detail=s.Services_GET_OkResponseSchema.description)
def unregister_service_view(request): """ Unregister a service. """ service = ar.get_service_matchdict_checked(request) service_push = asbool( ar.get_multiformat_delete(request, "service_push", default=False)) svc_content = sf.format_service(service, show_private_url=True) svc_res_id = service.resource_id ax.evaluate_call(lambda: models.resource_tree_service.delete_branch( resource_id=svc_res_id, db_session=request.db), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, 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) if svc_push and svc.type in SERVICES_PHOENIX_ALLOWED: sync_services_phoenix(db_session.query(models.Service)) ax.evaluate_call( lambda: remove_service_magpie_and_phoenix(service, service_push, request.db), fallback=lambda: request.db.rollback(), httpError=HTTPForbidden, msgOnFail=s.Service_DELETE_ForbiddenResponseSchema.description, content=svc_content) return ax.valid_http(httpSuccess=HTTPOk, detail=s.Service_DELETE_OkResponseSchema.description)
def get_service_view(request): """ Get a service information. """ service = ar.get_service_matchdict_checked(request) service_info = sf.format_service(service, show_private_url=True, show_resources_allowed=True, show_configuration=True) return ax.valid_http(http_success=HTTPOk, detail=s.Service_GET_OkResponseSchema.description, content={"service": service_info})
def get_user_services(user, request, cascade_resources=False, inherit_groups_permissions=False, format_as_list=False): # type: (models.User, Request, bool, bool, bool) -> UserServicesType """ Returns services by type with corresponding services by name containing sub-dict information. :param user: user for which to find services :param request: request with database session connection :param cascade_resources: If `False`, return only services with *Direct* user permissions on their corresponding service-resource. Otherwise, return every service that has at least one sub-resource with user permissions. :param inherit_groups_permissions: If `False`, return only user-specific service/sub-resources permissions. Otherwise, resolve inherited permissions using all groups the user is member of. :param format_as_list: returns as list of service dict information (not grouped by type and by name) :return: only services which the user as *Direct* or *Inherited* permissions, according to `inherit_from_resources` :rtype: dict of services by type with corresponding services by name containing sub-dict information, unless `format_as_dict` is `True` """ db_session = request.db resource_type = None if cascade_resources else [ models.Service.resource_type ] res_perm_dict = get_user_resources_permissions_dict( user, resource_types=resource_type, request=request, inherit_groups_permissions=inherit_groups_permissions) services = {} for resource_id, perms in res_perm_dict.items(): svc = ResourceService.by_resource_id(resource_id=resource_id, db_session=db_session) if svc.resource_type != models.Service.resource_type and cascade_resources: svc = get_resource_root_service(svc, request) perms = svc.permissions if svc.type not in services: services[svc.type] = {} if svc.resource_name not in services[svc.type]: services[svc.type][svc.resource_name] = format_service( svc, perms, show_private_url=False) if not format_as_list: return services services_list = list() for svc_type in services: for svc_name in services[svc_type]: services_list.append(services[svc_type][svc_name]) return services_list
def get_service_permissions_view(request): """ List all applicable permissions for a service. """ service = ar.get_service_matchdict_checked(request) svc_content = sf.format_service(service, show_private_url=True) svc_perms = ax.evaluate_call(lambda: [p.value for p in SERVICE_TYPE_DICT[service.type].permissions], fallback=request.db.rollback(), http_error=HTTPBadRequest, content=svc_content, msg_on_fail=s.ServicePermissions_GET_BadRequestResponseSchema.description) return ax.valid_http(http_success=HTTPOk, detail=s.ServicePermissions_GET_OkResponseSchema.description, content=format_permissions(svc_perms, PermissionType.ALLOWED))
def get_group_services(resources_permissions_dict, db_session): # type: (JSON, Session) -> JSON """ Nest and regroup the resource permissions under corresponding root service types. """ grp_svc_dict = {} for res_id, perms in resources_permissions_dict.items(): svc = ResourceService.by_resource_id(resource_id=res_id, db_session=db_session) svc_type = str(svc.type) svc_name = str(svc.resource_name) if svc_type not in grp_svc_dict: grp_svc_dict[svc_type] = {} grp_svc_dict[svc_type][svc_name] = format_service(svc, perms, show_private_url=False) return grp_svc_dict
def get_group_service_permissions_response(group, service, db_session): # type: (models.Group, models.Service, Session) -> HTTPException """ Get validated response of found group service permissions. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. """ svc_perms_found = ax.evaluate_call( lambda: format_permissions(get_group_service_permissions(group, service, db_session)), httpError=HTTPInternalServerError, msgOnFail=s.GroupServicePermissions_GET_InternalServerErrorResponseSchema.description, content={u"group": format_group(group, basic_info=True), u"service": format_service(service)}) return ax.valid_http(httpSuccess=HTTPOk, detail=s.GroupServicePermissions_GET_OkResponseSchema.description, content={u"permission_names": svc_perms_found})
def create_service(service_name, service_type, service_url, service_push, service_config, db_session): # type: (Str, Str, Str, bool, Optional[JSON], Session) -> HTTPException """ Generates an instance to register a new service. """ def _add_service_magpie_and_phoenix(svc, svc_push, db): db.add(svc) if svc_push and svc.type in SERVICES_PHOENIX_ALLOWED: sync_services_phoenix(db.query(models.Service)) # sometimes, resource ID is not updated, fetch the service to obtain it if not svc.resource_id: svc = ax.evaluate_call( lambda: models.Service.by_service_name(service_name, db_session=db_session), fallback=lambda: db_session.rollback(), http_error=HTTPInternalServerError, msg_on_fail=s.Services_POST_InternalServerErrorResponseSchema. description, content={ "service_name": str(service_name), "resource_id": svc.resource_id }) ax.verify_param( svc.resource_id, not_none=True, param_compare=int, is_type=True, http_error=HTTPInternalServerError, msg_on_fail=s.Services_POST_InternalServerErrorResponseSchema. description, content={ "service_name": str(service_name), "resource_id": svc.resource_id }, param_name="service_name") return svc ax.verify_param( service_type, not_none=True, not_empty=True, is_type=True, param_name="service_type", param_compare=six.string_types, http_error=HTTPBadRequest, msg_on_fail=s.Services_POST_BadRequestResponseSchema.description) ax.verify_param( service_type, is_in=True, param_compare=SERVICE_TYPE_DICT.keys(), param_name="service_type", http_error=HTTPBadRequest, msg_on_fail=s.Services_POST_BadRequestResponseSchema.description) ax.verify_param(service_url, matches=True, param_compare=ax.URL_REGEX, param_name="service_url", http_error=HTTPBadRequest, msg_on_fail=s. Services_POST_Params_BadRequestResponseSchema.description) ax.verify_param(service_name, not_empty=True, not_none=True, matches=True, param_name="service_name", param_compare=ax.PARAM_REGEX, http_error=HTTPBadRequest, msg_on_fail=s. Services_POST_Params_BadRequestResponseSchema.description) ax.verify_param( models.Service.by_service_name(service_name, db_session=db_session), is_none=True, param_name="service_name", with_param=False, content={"service_name": str(service_name)}, http_error=HTTPConflict, msg_on_fail=s.Services_POST_ConflictResponseSchema.description) if service_config is not None: ax.verify_param( service_config, param_name="configuration", param_compare=dict, is_type=True, http_error=HTTPUnprocessableEntity, msg_on_fail=s. Service_CheckConfig_UnprocessableEntityResponseSchema.description) service = ax.evaluate_call( lambda: models.Service(resource_name=str(service_name), resource_type=models.Service.resource_type_name, configuration=service_config, url=str(service_url), type=str(service_type)), # noqa fallback=lambda: db_session.rollback(), http_error=HTTPForbidden, msg_on_fail=s.Services_POST_UnprocessableEntityResponseSchema. description, content={ "service_name": str(service_name), "resource_type": models.Service.resource_type_name, "service_url": str(service_url), "service_type": str(service_type) }) service = ax.evaluate_call( lambda: _add_service_magpie_and_phoenix(service, service_push, db_session), fallback=lambda: db_session.rollback(), http_error=HTTPForbidden, msg_on_fail=s.Services_POST_ForbiddenResponseSchema.description, content=format_service(service, show_private_url=True)) return ax.valid_http( http_success=HTTPCreated, detail=s.Services_POST_CreatedResponseSchema.description, content={"service": format_service(service, show_private_url=True)})
def create_service(service_name, service_type, service_url, service_push, db_session): # type: (Str, Str, Str, bool, Session) -> HTTPException """ Generates an instance to register a new service. """ def _add_service_magpie_and_phoenix(svc, svc_push, db): db.add(svc) if svc_push and svc.type in SERVICES_PHOENIX_ALLOWED: sync_services_phoenix(db.query(models.Service)) # sometimes, resource ID is not updated, fetch the service to obtain it if not svc.resource_id: svc = ax.evaluate_call( lambda: models.Service.by_service_name(service_name, db_session=db_session), fallback=lambda: db_session.rollback(), httpError=HTTPInternalServerError, msgOnFail=s.Services_POST_InternalServerErrorResponseSchema. description, content={ u'service_name': str(service_name), u'resource_id': svc.resource_id }) ax.verify_param( svc.resource_id, notNone=True, paramCompare=int, ofType=True, httpError=HTTPInternalServerError, msgOnFail=s.Services_POST_InternalServerErrorResponseSchema. description, content={ u'service_name': str(service_name), u'resource_id': svc.resource_id }, paramName=u'service_name') return svc # noinspection PyArgumentList service = ax.evaluate_call( lambda: models.Service(resource_name=str(service_name), resource_type=models.Service.resource_type_name, url=str(service_url), type=str(service_type)), fallback=lambda: db_session.rollback(), httpError=HTTPForbidden, msgOnFail=s.Services_POST_UnprocessableEntityResponseSchema. description, content={ u'service_name': str(service_name), u'resource_type': models.Service.resource_type_name, u'service_url': str(service_url), u'service_type': str(service_type) }) service = ax.evaluate_call( lambda: _add_service_magpie_and_phoenix(service, service_push, db_session), fallback=lambda: db_session.rollback(), httpError=HTTPForbidden, msgOnFail=s.Services_POST_ForbiddenResponseSchema.description, content=format_service(service, show_private_url=True)) return ax.valid_http( httpSuccess=HTTPCreated, detail=s.Services_POST_CreatedResponseSchema.description, content={u'service': format_service(service, show_private_url=True)})
def get_user_services(user, request, cascade_resources=False, format_as_list=False, inherit_groups_permissions=False, resolve_groups_permissions=False): # type: (models.User, Request, bool, bool, bool, bool) -> UserServicesType """ Returns services by type with corresponding services by name containing sub-dict information. .. seealso:: :func:`regroup_permissions_by_resource` :param user: user for which to find services :param request: request with database session connection :param cascade_resources: If ``False``, return only services which the :term:`User` has :term:`Immediate Permissions` on specialized top-level resources corresponding to a :term:`Service`. Otherwise, return every service that has at least one sub-resource with permissions (children at any-level). In both cases, the *permissions* looked for consider either only :term:`Direct Permissions` or any :term:`Inherited Permissions` according to the value of :paramref:`inherit_groups_permissions`. :param inherit_groups_permissions: If ``False``, return only user-specific service/sub-resources :term:`Direct Permissions`. Otherwise, resolve :term:`Inherited Permissions` using all groups the user is member of. :param resolve_groups_permissions: Whether to combine :term:`Direct Permissions` and :term:`Inherited Permissions` for respective resources or not. :param format_as_list: returns as list of service dict information (not grouped by type and by name) :return: Only services which the user as :term:`Direct Permissions` or considering all tree hierarchy, and for each case, either considering only user permissions or every :term:`Inherited Permissions`, according to provided options. :rtype: Mapping of services by type to corresponding services by name containing each sub-mapping of their information, unless :paramref:`format_as_list` is ``True``, in which case a flat list of service information is returned. """ db_session = request.db resource_type = None if cascade_resources else [models.Service.resource_type] res_perm_dict = get_user_resources_permissions_dict(user, resource_types=resource_type, request=request, inherit_groups_permissions=inherit_groups_permissions, resolve_groups_permissions=resolve_groups_permissions) perm_type = PermissionType.INHERITED if inherit_groups_permissions else PermissionType.DIRECT services = {} for resource_id, perms in res_perm_dict.items(): resource = ResourceService.by_resource_id(resource_id=resource_id, db_session=db_session) is_service = resource.resource_type == models.Service.resource_type_name if not is_service: # if any children resource had user/group permissions, minimally return its root service without # any immediate permission, otherwise (cascade off) it is skipped and not returned at all in response if not cascade_resources: continue perms = [] svc = ru.get_resource_root_service_impl(resource, request) if svc.service_type not in services: services[svc.service_type] = {} svc_name = svc.service.resource_name svc_type = svc.service_type # if service was not already added, add it (could be directly its permissions, or empty via children resource) # otherwise, set explicit immediate permissions on service instead of empty children resource permissions if svc_name not in services[svc_type] or is_service: svc_json = format_service(svc.service, perms, perm_type, show_private_url=False) services[svc_type][svc_name] = svc_json if not format_as_list: return services services_list = list() for svc_type in services: for svc_name in services[svc_type]: services_list.append(services[svc_type][svc_name]) return services_list
def magpie_register_permissions_from_config(permissions_config, magpie_url=None, db_session=None): # type: (Union[Str, ConfigDict], Optional[Str], Optional[Session]) -> None """ Applies permissions specified in configuration. :param permissions_config: file path to 'permissions' config or JSON/YAML equivalent pre-loaded. :param magpie_url: URL to magpie instance (when using requests; default: `magpie.url` from this app's config). :param db_session: db session to use instead of requests to directly create/remove permissions with config. .. seealso:: `magpie/permissions.cfg` for specific parameters and operational details. """ permissions = _load_config(permissions_config, "permissions") if not permissions: LOGGER.warning("Permissions configuration are empty.") return if use_request(db_session): magpie_url = magpie_url or get_magpie_url() settings = {'magpie.url': magpie_url} logging.debug( "Editing permissions using requests to [{}]...".format(magpie_url)) err_msg = "Invalid credentials to register Magpie permissions." cookies_or_session = get_admin_cookies(settings, raise_message=err_msg) else: logging.debug("Editing permissions using db session...") cookies_or_session = db_session logging.info("Found {} permissions to update.".format(len(permissions))) for i, perm_cfg in enumerate(permissions): # parameter validation if not isinstance(perm_cfg, dict) or not all( f in perm_cfg for f in ["permission", "service"]): warn_permission( "Invalid permission format for [{!s}]".format(perm_cfg), i) continue if perm_cfg["permission"] not in Permission.values(): warn_permission( "Unknown permission [{!s}]".format(perm_cfg['permission']), i) continue usr_name = perm_cfg.get("user") grp_name = perm_cfg.get("group") if not any([usr_name, grp_name]): warn_permission("Missing required user and/or group field.", i) continue if "action" not in perm_cfg: warn_permission("Unspecified action", i, trail="using default (create)...") perm_cfg["action"] = "create" if perm_cfg["action"] not in ["create", "remove"]: warn_permission("Unknown action [{!s}]".format(perm_cfg["action"]), i) continue # retrieve service for permissions validation svc_name = perm_cfg["service"] if use_request(cookies_or_session): svc_path = magpie_url + ServiceAPI.path.format( service_name=svc_name) svc_resp = requests.get(svc_path, cookies=cookies_or_session) if svc_resp.status_code != 200: warn_permission("Unknown service [{!s}]".format(svc_name), i) continue service_info = get_json(svc_resp)[svc_name] else: transaction.commit( ) # force any pending transaction to be applied to find possible dependencies svc = models.Service.by_service_name(svc_name, db_session=cookies_or_session) if not svc: warn_permission( "Unknown service [{!s}]. Can't edit permissions without service." .format(svc_name), i) continue from magpie.api.management.service.service_formats import format_service service_info = format_service(svc) # apply permission config resource_id, found = parse_resource_path(perm_cfg, i, service_info, cookies_or_session, magpie_url) if found: if not resource_id: resource_id = service_info["resource_id"] apply_permission_entry(perm_cfg, i, resource_id, cookies_or_session, magpie_url) if not use_request(cookies_or_session): transaction.commit() logging.info("Done processing permissions.")
def update_service_view(request): """ Update a service information. """ service = ar.get_service_matchdict_checked(request) service_push = asbool( ar.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 # None/Empty values are accepted in case of unspecified svc_name = select_update(ar.get_multiformat_post(request, "service_name"), service.resource_name) svc_url = select_update(ar.get_multiformat_post(request, "service_url"), service.url) ax.verify_param( svc_name, paramCompare="types", notEqual=True, paramName="service_name", httpError=HTTPBadRequest, msgOnFail=s.Service_PUT_BadRequestResponseSchema_ReservedKeyword. description) ax.verify_param( svc_name == service.resource_name and svc_url == service.url, notEqual=True, paramCompare=True, paramName="service_name/service_url", httpError=HTTPBadRequest, msgOnFail=s.Service_PUT_BadRequestResponseSchema.description) if svc_name != service.resource_name: all_svc_names = list() for svc_type in SERVICE_TYPE_DICT: for svc in su.get_services_by_type(svc_type, db_session=request.db): all_svc_names.append(svc.resource_name) ax.verify_param( svc_name, notIn=True, paramCompare=all_svc_names, httpError=HTTPConflict, msgOnFail=s.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 _svc.url = new_url has_getcap = Permission.GET_CAPABILITIES in SERVICE_TYPE_DICT[ _svc.type].permissions if svc_push and svc.type in SERVICES_PHOENIX_ALLOWED and has_getcap: # (re)apply getcapabilities to updated service to ensure updated push su.add_service_getcapabilities_perms(_svc, db_session) sync_services_phoenix(db_session.query( models.Service)) # push all services old_svc_content = sf.format_service(service, show_private_url=True) err_svc_content = { u"service": old_svc_content, u"new_service_name": svc_name, u"new_service_url": svc_url } ax.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=s.Service_PUT_ForbiddenResponseSchema.description, content=err_svc_content) return ax.valid_http(httpSuccess=HTTPOk, detail=s.Service_PUT_OkResponseSchema.description, content={ u"service": sf.format_service(service, show_private_url=True) })