def resource_name(self, db_session=None): db_session = get_db_session(db_session) if self.resource_id: return ResourceService.by_resource_id( self.resource_id, db_session=db_session).resource_name else: return "any resource"
def __init__(self, request): self.__acl__ = [] self.used_uuid = False # used_uuid is set to true if user who is normally not authorized to # view the resource gains access to it because owner set it to public # and user knows the uuid of object org_resource_id = request.matchdict.get("resource_id", request.GET.get("resource_id")) resource_id = to_integer_safe(org_resource_id) self.resource = (ResourceService.by_resource_id(resource_id) if resource_id else None) if self.resource is None: self.resource = DashboardService.by_uuid(org_resource_id) if self.resource and request.user: self.__acl__ = self.resource.__acl__ permissions = ResourceService.perms_for_user( self.resource, request.user) for perm_user, perm_name in permission_to_04_acls(permissions): self.__acl__.append(rewrite_root_perm(perm_user, perm_name)) if self.resource and self.resource.public: if not request.has_permission("view", self): self.used_uuid = True self.__acl__.append((Allow, Everyone, "view")) add_root_superperm(request, self)
def send_alerts(self, request=None, resource=None, db_session=None): """" Sends alerts to applicable channels """ db_session = get_db_session(db_session) db_session.flush() if not resource: resource = ResourceService.by_resource_id(self.resource_id) if not request: request = get_current_request() if not resource: return users = set( [p.user for p in ResourceService.users_for_perm(resource, "view")]) for user in users: for channel in user.alert_channels: matches_resource = not channel.resources or resource in [ r.resource_id for r in channel.resources ] if (not channel.channel_validated or not channel.send_alerts or not matches_resource): continue else: try: channel.notify_alert(resource=resource, event=self, user=user, request=request) except IntegrationException as e: log.warning("%s" % e)
def __init__(self, request): Resource = appenlight.models.resource.Resource self.__acl__ = [] group_id = request.matchdict.get("group_id", request.params.get("group_id")) group_id = to_integer_safe(group_id) self.report_group = ReportGroupService.by_id(group_id) if group_id else None if not self.report_group: raise HTTPNotFound() self.public = self.report_group.public self.resource = ( ResourceService.by_resource_id(self.report_group.resource_id) if self.report_group else None ) if self.resource: self.__acl__ = self.resource.__acl__ if request.user: permissions = ResourceService.perms_for_user(self.resource, request.user) for perm_user, perm_name in permission_to_04_acls(permissions): self.__acl__.append(rewrite_root_perm(perm_user, perm_name)) if self.public: self.__acl__.append((Allow, Everyone, "view")) if not request.user: # unauthed users need to visit using both group and report pair report_id = request.params.get( "reportId", request.params.get("report_id", -1) ) report = self.report_group.get_report(report_id, public=True) if not report: raise HTTPNotFound() add_root_superperm(request, self)
def __init__(self, request): self.__acl__ = [] self.used_uuid = False # used_uuid is set to true if user who is normally not authorized to # view the resource gains access to it because owner set it to public # and user knows the uuid of object alert_id = request.matchdict.get("alert_id", request.GET.get("alert_id")) self.alert = AlertChannelActionService.by_pkey(alert_id) if not self.alert: raise HTTPNotFound() self.chart = DashboardChartService.by_uuid(self.alert.other_id) if not self.chart: raise HTTPNotFound() self.resource = ResourceService.by_resource_id(self.chart.resource_id) if self.resource and request.user: self.__acl__ = self.resource.__acl__ permissions = ResourceService.perms_for_user( self.resource, request.user) for perm_user, perm_name in permission_to_04_acls(permissions): self.__acl__.append(rewrite_root_perm(perm_user, perm_name)) if self.resource and self.resource.public: if not request.has_permission("view", self): self.used_uuid = True self.__acl__.append((Allow, Everyone, "view")) add_root_superperm(request, self)
def by_resource_name(name,db_session=None): db_session = get_db_session(db_session) try: resource = db_session.query(Resource).filter(Resource.resource_name==name).one() log.debug(resource) return ResourceService.by_resource_id(resource_id=resource.resource_id, db_session=db_session) except Exception as e: log.debug('Error retrieving resource by name, {0}'.format(e)) raise
def by_resource_name(name, db_session=None): db_session = get_db_session(db_session) try: resource = db_session.query(Resource).filter( Resource.resource_name == name).one() log.debug(resource) return ResourceService.by_resource_id( resource_id=resource.resource_id, db_session=db_session) except Exception as e: log.debug('Error retrieving resource by name, {0}'.format(e)) raise
def by_resource_id(cls, resource_id, db_session=None): """ .. deprecated:: 0.8 :param resource_id: :param db_session: :return: """ db_session = get_db_session(db_session) return ResourceService.by_resource_id(resource_id=resource_id, db_session=db_session)
def get_resource_root_service_by_id(resource_id, db_session): # type: (ServiceOrResourceType, Session) -> Optional[models.Service] """ Retrieves the service-specialized resource corresponding to the top-level resource in the tree hierarchy. .. seealso:: - :func:`get_resource_root_service` for same operation but directly using the resource """ resource = ResourceService.by_resource_id(resource_id, db_session=db_session) if resource is None: return None return get_resource_root_service(resource, db_session=db_session)
def __init__(self, request): Resource = appenlight.models.resource.Resource self.__acl__ = [] resource_id = request.unsafe_json_body().get("resource_id") resource_id = to_integer_safe(resource_id) self.resource = ResourceService.by_resource_id(resource_id) if self.resource and request.user: self.__acl__ = self.resource.__acl__ permissions = ResourceService.perms_for_user(self.resource, request.user) for perm_user, perm_name in permission_to_04_acls(permissions): self.__acl__.append(rewrite_root_perm(perm_user, perm_name)) add_root_superperm(request, self)
def get_resource_root_service(resource, db_session): # type: (ServiceOrResourceType, Session) -> Optional[models.Service] """ Retrieves the service-specialized resource corresponding to the top-level resource in the tree hierarchy. .. seealso:: - :func:`get_resource_root_service_by_id` for same operation but using the resource ID - :func:`get_resource_root_service_impl` to retrieve the explicit service's implementation """ if resource is not None: if resource.resource_type == models.Service.resource_type_name: return resource return ResourceService.by_resource_id(resource.root_service_id, db_session=db_session) return None
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 __init__(self, request): Resource = appenlight.models.resource.Resource self.__acl__ = [] self.resource = None plugin_id = to_integer_safe(request.matchdict.get("id")) self.plugin = PluginConfigService.by_id(plugin_id) if not self.plugin: raise HTTPNotFound() if self.plugin.resource_id: self.resource = ResourceService.by_resource_id(self.plugin.resource_id) if self.resource: self.__acl__ = self.resource.__acl__ if request.user and self.resource: permissions = ResourceService.perms_for_user(self.resource, request.user) for perm_user, perm_name in permission_to_04_acls(permissions): self.__acl__.append(rewrite_root_perm(perm_user, perm_name)) add_root_superperm(request, self)
def get_resource_permissions(resource, db_session): # type: (ServiceOrResourceType, Session) -> List[Permission] """ Obtains the applicable permissions on the service or resource, accordingly to what was provided. When parsing a resource, rewinds the hierarchy up to the top-most service in order to find the context under which the resource resides, and therefore which permissions this resource is allowed to have under that service. """ ax.verify_param( resource, not_none=True, http_error=HTTPBadRequest, param_name="resource", msg_on_fail=s. UserResourcePermissions_GET_BadRequestResourceResponseSchema. description) # directly access the service resource if resource.root_service_id is None: service = resource # type: models.Service # noqa return SERVICE_TYPE_DICT[service.type].permissions # otherwise obtain root level service to infer sub-resource permissions service = ResourceService.by_resource_id(resource.root_service_id, db_session=db_session) ax.verify_param( service.resource_type, is_equal=True, http_error=HTTPBadRequest, param_name="resource_type", param_compare=models.Service.resource_type_name, msg_on_fail=s. UserResourcePermissions_GET_BadRequestRootServiceResponseSchema. description) service_class = SERVICE_TYPE_DICT[service.type] ax.verify_param( resource.resource_type_name, is_in=True, http_error=HTTPBadRequest, param_name="resource_type", param_compare=service_class.resource_type_names, msg_on_fail=s. UserResourcePermissions_GET_BadRequestResourceTypeResponseSchema. description) return service_class.get_resource_permissions(resource.resource_type_name)
def get_resource_matchdict_checked(request, resource_name_key="resource_id"): # type: (Request, Str) -> models.Resource """ Obtains the resource matched against the specified request path variable. :returns: found resource. :raises HTTPForbidden: if the requesting user does not have sufficient permission to execute this request. :raises HTTPNotFound: if the specified resource ID does not correspond to any existing resource. """ resource_id = get_value_matchdict_checked(request, resource_name_key, pattern=ax.INDEX_REGEX) resource_id = ax.evaluate_call(lambda: int(resource_id), http_error=HTTPBadRequest, msg_on_fail=s.Resource_MatchDictCheck_BadRequestResponseSchema.description) resource = ax.evaluate_call(lambda: ResourceService.by_resource_id(resource_id, db_session=request.db), fallback=lambda: request.db.rollback(), http_error=HTTPForbidden, msg_on_fail=s.Resource_MatchDictCheck_ForbiddenResponseSchema.description) ax.verify_param(resource, not_none=True, http_error=HTTPNotFound, param_content={"value": resource_id}, param_name="resource_id", msg_on_fail=s.Resource_MatchDictCheck_NotFoundResponseSchema.description) return resource
def recursive_fmt_res_tree(children_dict): fmt_res_tree = {} for child_id, child_dict in children_dict.items(): resource = child_dict["node"] new_children = child_dict["children"] perms = [] # case of pre-specified user/group-specific permissions if resources_perms_dict is not None: if resource.resource_id in resources_perms_dict.keys(): perms = resources_perms_dict[resource.resource_id] # case of full fetch (allowed resource permissions) else: # directly access the resource if it is a service service = None # type: Optional[Service] if resource.root_service_id is None: service = resource service_id = resource.resource_id # obtain corresponding top-level service resource if not already available, # get resource permissions allowed under the top service's scope else: service_id = resource.root_service_id if service_id not in __internal_svc_res_perm_dict: service = ResourceService.by_resource_id( service_id, db_session=db_session) # add to dict only if not already added if service is not None and service_id not in __internal_svc_res_perm_dict: __internal_svc_res_perm_dict[service_id] = { res_type.resource_type_name: res_perms # use str key to match below 'resource_type' field for res_type, res_perms in SERVICE_TYPE_DICT[ service.type].resource_types_permissions.items() } perms = __internal_svc_res_perm_dict[service_id][ resource.resource_type] # 'resource_type' is str here fmt_res_tree[child_id] = format_resource(resource, perms, permission_type) fmt_res_tree[child_id]["children"] = recursive_fmt_res_tree( new_children) return fmt_res_tree
def __init__(self, request): self.__acl__ = [] resource_id = safe_integer(request.matchdict.get("object_id")) self.resource = ResourceService.by_resource_id( resource_id, db_session=request.dbsession) if not self.resource: raise HTTPNotFound() if self.resource: self.__acl__ = self.resource.__acl__ if self.resource and request.user: # add perms that this user has for this resource # this is a big performance optimization - we fetch only data # needed to check one specific user permissions = ResourceService.perms_for_user( self.resource, request.user) for outcome, perm_user, perm_name in permission_to_pyramid_acls( permissions): self.__acl__.append( rewrite_root_perm(outcome, perm_user, perm_name)) allow_root_access(request, context=self)
def validate_or_close(self, since_when, db_session=None): """ Checks if alerts should stay open or it's time to close them. Generates close alert event if alerts get closed """ event_types = [ Event.types["error_report_alert"], Event.types["slow_report_alert"], ] app = ResourceService.by_resource_id(self.resource_id) # if app was deleted close instantly if not app: self.close() return if self.event_type in event_types: total = ReportStatService.count_by_type(self.event_type, self.resource_id, since_when) if Event.types["error_report_alert"] == self.event_type: threshold = app.error_report_threshold if Event.types["slow_report_alert"] == self.event_type: threshold = app.slow_report_threshold if total < threshold: self.close()
def create_resource(resource_name, resource_display_name, resource_type, parent_id, db_session): # type: (Str, Optional[Str], Str, int, Session) -> HTTPException ax.verify_param( resource_name, param_name="resource_name", not_none=True, not_empty=True, http_error=HTTPUnprocessableEntity, msg_on_fail= "Invalid 'resource_name' specified for child resource creation.") ax.verify_param( resource_type, param_name="resource_type", not_none=True, not_empty=True, http_error=HTTPUnprocessableEntity, msg_on_fail= "Invalid 'resource_type' specified for child resource creation.") ax.verify_param( parent_id, param_name="parent_id", not_none=True, is_type=True, param_compare=int, http_error=HTTPUnprocessableEntity, msg_on_fail="Invalid 'parent_id' specified for child resource creation." ) parent_resource = ax.evaluate_call( lambda: ResourceService.by_resource_id(parent_id, db_session=db_session), fallback=lambda: db_session.rollback(), http_error=HTTPNotFound, msg_on_fail=s.Resources_POST_NotFoundResponseSchema.description, content={ "parent_id": parent_id, "resource_name": str(resource_name), "resource_type": str(resource_type) }) # verify for valid permissions from top-level service-specific corresponding resources permissions root_service = check_valid_service_resource(parent_resource, resource_type, db_session) new_resource = models.resource_factory( resource_type=resource_type, resource_name=resource_name, resource_display_name=resource_display_name or resource_name, root_service_id=root_service.resource_id, parent_id=parent_resource.resource_id) # two resources with the same parent can't have the same name err_msg = s.Resources_POST_ConflictResponseSchema.description check_unique_child_resource_name(resource_name, parent_id, err_msg, db_session=db_session) def add_resource_in_tree(new_res, db): db_session.add(new_res) total_children = models.RESOURCE_TREE_SERVICE.count_children( new_res.parent_id, db_session=db) models.RESOURCE_TREE_SERVICE.set_position( resource_id=new_res.resource_id, to_position=total_children, db_session=db) ax.evaluate_call( lambda: add_resource_in_tree(new_resource, db_session), fallback=lambda: db_session.rollback(), http_error=HTTPForbidden, msg_on_fail=s.Resources_POST_ForbiddenResponseSchema.description) return ax.valid_http( http_success=HTTPCreated, detail=s.Resources_POST_CreatedResponseSchema.description, content={"resource": format_resource(new_resource, basic_info=True)})
def effective_permissions(self, user, resource, permissions=None, allow_match=True): # type: (models.User, ServiceOrResourceType, Optional[Collection[Permission]], bool) -> List[PermissionSet] """ Obtains the effective permissions the user has over the specified resource. Recursively rewinds the resource tree from the specified resource up to the top-most parent service the resource resides under (or directly if the resource is the service) and retrieve permissions along the way that should be applied to children when using scoped-resource inheritance. Rewinding of the tree can terminate earlier when permissions can be immediately resolved such as when more restrictive conditions enforce denied access. Both user and group permission inheritance is resolved simultaneously to tree hierarchy with corresponding allow and deny conditions. User :term:`Direct Permissions` have priority over all its groups :term:`Inherited Permissions`, and denied permissions have priority over allowed access ones. All applicable permissions on the resource (as defined by :meth:`allowed_permissions`) will have their resolution (Allow/Deny) provided as output, unless a specific subset of permissions is requested using :paramref:`permissions`. Other permissions are ignored in this case to only resolve requested ones. For example, this parameter can be used to request only ACL resolution from specific permissions applicable for a given request, as obtained by :meth:`permission_requested`. Permissions scoped as `match` can be ignored using :paramref:`allow_match`, such as when the targeted resource does not exist. .. seealso:: - :meth:`ServiceInterface.resource_requested` """ if not permissions: permissions = self.allowed_permissions(resource) requested_perms = set(permissions) # type: Set[Permission] effective_perms = dict() # type: Dict[Permission, PermissionSet] # immediately return all permissions if user is an admin db_session = self.request.db admin_group = get_constant("MAGPIE_ADMIN_GROUP", self.request) admin_group = GroupService.by_group_name(admin_group, db_session=db_session) if admin_group in user.groups: # noqa return [ PermissionSet(perm, access=Access.ALLOW, scope=Scope.MATCH, typ=PermissionType.EFFECTIVE, reason=PERMISSION_REASON_ADMIN) for perm in permissions ] # level at which last permission was found, -1 if not found # employed to resolve with *closest* scope and for applicable 'reason' combination on same level effective_level = dict() # type: Dict[Permission, Optional[int]] current_level = 1 # one-based to avoid ``if level:`` check failing with zero full_break = False # current and parent resource(s) recursive-scope while resource is not None and not full_break: # bottom-up until service is reached # include both permissions set in database as well as defined directly on resource cur_res_perms = ResourceService.perms_for_user( resource, user, db_session=db_session) cur_res_perms.extend(permission_to_pyramid_acls(resource.__acl__)) for perm_name in requested_perms: if full_break: break for perm_tup in cur_res_perms: perm_set = PermissionSet(perm_tup) # if user is owner (directly or via groups), all permissions are set, # but continue processing this resource until end in case user explicit deny reverts it if perm_tup.perm_name == ALL_PERMISSIONS: # FIXME: # This block needs to be validated if support of ownership rules are added. # Conditions must be revised according to wanted behaviour... # General idea for now is that explict user/group deny should be prioritized over resource # ownership permissions since these can be attributed to *any user* while explicit deny are # definitely set by an admin-level user. for perm in requested_perms: if perm_set.access == Access.DENY: all_perm = PermissionSet( perm, perm_set.access, perm.scope, PermissionType.OWNED) effective_perms[perm] = all_perm else: all_perm = PermissionSet( perm, perm_set.access, perm.scope, PermissionType.OWNED) effective_perms.setdefault(perm, all_perm) full_break = True break # skip if the current permission must not be processed (at all or for the moment until next 'name') if perm_set.name not in requested_perms or perm_set.name != perm_name: continue # only first resource can use match (if even enabled with found one), parents are recursive-only if not allow_match and perm_set.scope == Scope.MATCH: continue # pick the first permission if none was found up to this point prev_perm = effective_perms.get(perm_name) scope_level = effective_level.get(perm_name) if not prev_perm: effective_perms[perm_name] = perm_set effective_level[perm_name] = current_level continue # user direct permissions have priority over inherited ones from groups # if inherited permission was found during previous iteration, override it with direct permission if perm_set.type == PermissionType.DIRECT: # - reset resolution scope of previous permission attributed to group as it takes precedence # - since there can't be more than one user permission-name per resource on a given level, # scope resolution is done after applying this *closest* permission, ignore higher level ones if prev_perm.type == PermissionType.INHERITED or not scope_level: effective_perms[perm_name] = perm_set effective_level[perm_name] = current_level continue # final decision for this user, skip any group permissions # resolve prioritized permission according to ALLOW/DENY, scope and group priority # (see 'PermissionSet.resolve' method for extensive details) # skip if last permission is not on group to avoid redundant USER > GROUP check processed before if prev_perm.type == PermissionType.INHERITED: # - If new permission to process is done against the previous permission from *same* tree-level, # there is a possibility to combine equal priority groups. In such case, reason is 'MULTIPLE'. # - If not of equal priority, the appropriate permission is selected and reason is overridden # accordingly by the new higher priority permission. # - If no permission was defined at all (first occurrence), also set it using current permission if scope_level in [None, current_level]: resolved_perm = PermissionSet.resolve( perm_set, prev_perm, context=PermissionType.EFFECTIVE) effective_perms[perm_name] = resolved_perm effective_level[perm_name] = current_level # - If new permission is at *different* tree-level, it applies only if the group has higher # priority than the previous one, to respect the *closest* scope to the target resource. # Same priorities are ignored as they were already resolved by *closest* scope above. # - Reset scope level with new permission such that another permission of same group priority as # that could be processed in next iteration can be compared against it, to resolve 'access' # priority between them. elif perm_set.group_priority > prev_perm.group_priority: effective_perms[perm_name] = perm_set effective_level[perm_name] = current_level # don't bother moving to parent if everything is resolved already # can only assume nothing left to resolve if all permissions are direct on user (highest priority) # if any found permission is group inherited, higher level user permission could still override it if (len(effective_perms) == len(requested_perms) and all(perm.type == PermissionType.DIRECT for perm in effective_perms.values())): break # otherwise, move to parent if any available, since we are not done rewinding the resource tree allow_match = False # reset match not applicable anymore for following parent resources current_level += 1 if resource.parent_id: resource = ResourceService.by_resource_id( resource.parent_id, db_session=db_session) else: resource = None # set deny for all still unresolved permissions from requested ones resolved_perms = set(effective_perms) missing_perms = set(permissions) - resolved_perms final_perms = set(effective_perms.values()) # type: Set[PermissionSet] for perm_name in missing_perms: perm = PermissionSet(perm_name, access=Access.DENY, scope=Scope.MATCH, typ=PermissionType.EFFECTIVE, reason=PERMISSION_REASON_DEFAULT) final_perms.add(perm) # enforce type and scope (use MATCH to make it explicit that it applies specifically for this resource) for perm in final_perms: perm.type = PermissionType.EFFECTIVE perm.scope = Scope.MATCH return list(final_perms)
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