def get_acl(self, sub_api_route=None): self.expand_acl(self.service, self.request.user) match_index = 0 route_parts = self.request.path.split("/") route_api_base = self.service.resource_name if sub_api_route is None else sub_api_route if self.service.resource_name in route_parts and route_api_base in route_parts: api_idx = route_parts.index(route_api_base) # keep only parts after api base route to process it if len(route_parts) - 1 > api_idx: route_parts = route_parts[api_idx + 1::] route_child = self.service # process read/write inheritance permission access while route_child and route_parts: part_name = route_parts.pop(0) route_res_id = route_child.resource_id route_child = models.find_children_by_name(part_name, parent_id=route_res_id, db_session=self.request.db) match_index = len(self.acl) self.expand_acl(route_child, self.request.user) # process read/write-match specific permission access # (convert exact route 'match' to read/write counterparts only if matching last item's permissions) for i in range(match_index, len(self.acl)): if Permission.get(self.acl[i][2]) == Permission.READ_MATCH: self.acl[i] = (self.acl[i][0], self.acl[i][1], Permission.READ.value) if Permission.get(self.acl[i][2]) == Permission.WRITE_MATCH: self.acl[i] = (self.acl[i][0], self.acl[i][1], Permission.WRITE.value) return self.acl
def check_valid_service_or_resource_permission(permission_name, service_or_resource, db_session): # type: (Union[Str, Permission], ServiceOrResourceType, Session) -> Optional[Permission] """ Checks if a permission is valid to be applied to a specific `service` or a `resource` under a root service. :param permission_name: permission name to be validated :param service_or_resource: resource item corresponding to either a Service or a Resource :param db_session: db connection :return: valid Permission if allowed by the service/resource :raises HTTPBadRequest: if the permission is not valid for the targeted service/resource """ svc_res_permissions = get_resource_permissions(service_or_resource, db_session=db_session) svc_res_type = service_or_resource.resource_type svc_res_name = service_or_resource.resource_name svc_res_perm = Permission.get(permission_name) ax.verify_param( svc_res_perm, param_name="permission_name", param_compare=svc_res_permissions, is_in=True, http_error=HTTPBadRequest, content={ "resource_type": str(svc_res_type), "resource_name": str(svc_res_name) }, msg_on_fail=s.UserResourcePermissions_POST_BadRequestResponseSchema. description) return svc_res_perm
def permission_requested(self): # type: () -> Permission try: req = self.parser.params[u"request"] perm = Permission.get(req) if perm is None: raise NotImplementedError("Undefined 'Permission' from 'request' parameter: {!s}".format(req)) return perm except KeyError as ex: # if 'ServiceInterface', 'params_expected' is empty and will raise a KeyError raise NotImplementedError("Exception: [{!r}] for class '{}'.".format(ex, type(self)))
def check_request(self, request): if request.path.startswith(self.twitcher_protected_path): service_name = parse_service_name(request.path, self.twitcher_protected_path) service = evaluate_call( lambda: Service.by_service_name(service_name, db_session=request.db), http_error=HTTPForbidden, msg_on_fail="Service query by name refused by db.") verify_param(service, not_none=True, http_error=HTTPNotFound, msg_on_fail="Service name not found.") # return a specific type of service, ex: ServiceWPS with all the acl (loaded according to the service_type) service_specific = service_factory(service, request) # should contain all the acl, this the only thing important # parse request (GET/POST) to get the permission requested for that service permission_requested = service_specific.permission_requested() # convert permission enum to str for comparison permission_requested = Permission.get( permission_requested).value if permission_requested else None if permission_requested: LOGGER.info("'%s' request '%s' permission on '%s'", request.user, permission_requested, request.path) self.update_request_cookies(request) authn_policy = request.registry.queryUtility( IAuthenticationPolicy) authz_policy = request.registry.queryUtility( IAuthorizationPolicy) principals = authn_policy.effective_principals(request) has_permission = authz_policy.permits(service_specific, principals, permission_requested) if LOGGER.isEnabledFor(logging.DEBUG): LOGGER.debug("%s - AUTHN policy configurations:", type(self).__name__) base_attr = [ attr for attr in dir(authn_policy.cookie) if not attr.startswith("_") ] for attr_name in base_attr: LOGGER.debug(" %s: %s", attr_name, getattr(authn_policy.cookie, attr_name)) if has_permission: return # allowed raise OWSAccessForbidden( "Not authorized to access this resource. " "User does not meet required permissions.")
def permission_requested(self): # type: () -> Permission try: req = str(self.parser.params["request"]).lower() perm = Permission.get(req) if perm is None: raise NotImplementedError( "Undefined 'Permission' from 'request' parameter: {!s}". format(req)) return perm except KeyError as exc: raise NotImplementedError( "Exception: [{!r}] for class '{}'.".format(exc, type(self)))
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 apply_permission_entry( permission_config_entry, # type: ConfigItem entry_index, # type: int resource_id, # type: int cookies_or_session, # type: CookiesOrSessionType magpie_url, # type: Str ): # type: (...) -> None """ Applies the single permission entry retrieved from the permission configuration. Assumes that permissions fields where pre-validated. Permission is applied for the user/group/resource using request or db session accordingly to arguments. """ def _apply_request(_usr_name=None, _grp_name=None): """ Apply operation using HTTP request. """ action_oper = None if usr_name: action_oper = UserResourcePermissionsAPI.path.replace( "{user_name}", _usr_name) if grp_name: action_oper = GroupResourcePermissionsAPI.path.replace( "{group_name}", _grp_name) if not action_oper: return None if create_perm: action_func = requests.post action_path = "{url}{path}".format(url=magpie_url, path=action_oper) action_body = {"permission_name": perm_name} else: action_func = requests.delete action_path = "{url}{path}/{perm_name}".format(url=magpie_url, path=action_oper, perm_name=perm_name) action_body = {} action_path = action_path.format(resource_id=resource_id) action_resp = action_func(action_path, json=action_body, cookies=cookies_or_session) return action_resp def _apply_session(_usr_name=None, _grp_name=None): """ Apply operation using db session. """ from magpie.api.management.user import user_utils as ut from magpie.api.management.group import group_utils as gt res = ResourceService.by_resource_id(resource_id, db_session=cookies_or_session) if _usr_name: usr = UserService.by_user_name(_usr_name, db_session=cookies_or_session) if create_perm: return ut.create_user_resource_permission_response( usr, res, perm, db_session=cookies_or_session) else: return ut.delete_user_resource_permission_response( usr, res, perm, db_session=cookies_or_session) if _grp_name: grp = GroupService.by_group_name(_grp_name, db_session=cookies_or_session) if create_perm: return gt.create_group_resource_permission_response( grp, res, perm, db_session=cookies_or_session) else: return gt.delete_group_resource_permission_response( grp, res, perm, db_session=cookies_or_session) def _apply_profile(_usr_name=None, _grp_name=None): """ Creates the user/group profile as required. """ usr_data = { "user_name": _usr_name, "password": "******", "email": "{}@mail.com".format(_usr_name), "group_name": get_constant("MAGPIE_ANONYMOUS_GROUP") } if use_request(cookies_or_session): if _usr_name: path = "{url}{path}".format(url=magpie_url, path=UsersAPI.path) return requests.post(path, json=usr_data) if _grp_name: path = "{url}{path}".format(url=magpie_url, path=GroupsAPI.path) data = {"group_name": _grp_name} return requests.post(path, json=data) else: if _usr_name: from magpie.api.management.user.user_utils import create_user usr_data[ 'db_session'] = cookies_or_session # back-compatibility python 2 cannot have kw after **unpack return create_user(**usr_data) if _grp_name: from magpie.api.management.group.group_utils import create_group return create_group(_grp_name, cookies_or_session) def _validate_response(operation, is_create, item_type="Permission"): """ Validate action/operation applied. """ # handle HTTPException raised if not islambda(operation): raise Exception("invalid use of method") try: _resp = operation() if _resp is None: return except HTTPException as exc: _resp = exc except Exception: raise # validation according to status code returned if is_create: if _resp.status_code == 201: warn_permission("{} successfully created.".format(item_type), entry_index, level=logging.INFO, trail='') elif _resp.status_code == 409: warn_permission("{} already exists.".format(item_type), entry_index, level=logging.INFO) else: warn_permission("Unknown response [{}]".format( _resp.status_code), entry_index, permission=permission_config_entry, level=logging.ERROR) else: if _resp.status_code == 200: warn_permission("{} successfully removed.".format(item_type), entry_index, level=logging.INFO, trail='') elif _resp.status_code == 404: warn_permission("{} already removed.".format(item_type), entry_index, level=logging.INFO) else: warn_permission("Unknown response [{}]".format( _resp.status_code), entry_index, permission=permission_config_entry, level=logging.ERROR) create_perm = permission_config_entry["action"] == "create" perm_name = permission_config_entry["permission"] usr_name = permission_config_entry.get("user") grp_name = permission_config_entry.get("group") perm = Permission.get(perm_name) _validate_response(lambda: _apply_profile(usr_name, None), is_create=True) _validate_response(lambda: _apply_profile(None, grp_name), is_create=True) if use_request(cookies_or_session): _validate_response(lambda: _apply_request(usr_name, None), is_create=create_perm) _validate_response(lambda: _apply_request(None, grp_name), is_create=create_perm) else: _validate_response(lambda: _apply_session(usr_name, None), is_create=create_perm) _validate_response(lambda: _apply_session(None, grp_name), is_create=create_perm)