def permissions_get(request): # Invert the permissions inheritance tree. perms_descending_tree = {} for obtained, obtained_from in PERMISSIONS_INHERITANCE_TREE.items(): on_resource, obtained_perm = obtained.split(':', 1) for from_resource, perms in obtained_from.items(): for perm in perms: perms_descending_tree.setdefault(from_resource, {})\ .setdefault(perm, {})\ .setdefault(on_resource, set())\ .add(obtained_perm) # Obtain current principals. principals = request.effective_principals if Authenticated in principals: # Since this view does not require any permission (can be used to # obtain public users permissions), we have to add the prefixed userid # among the principals (see :mode:`kinto.core.authentication`) userid = request.prefixed_userid principals.append(userid) # Query every possible permission of the current user from backend. # Since there is no "full-list" method, we query for each possible # permission (read, write, group:create, collection:create, record:create). # XXX: could be optimized into one call to backend when needed. possible_perms = set([k.split(':', 1)[1] for k in PERMISSIONS_INHERITANCE_TREE.keys()]) backend = request.registry.permission perms_by_object_uri = {} for perm in possible_perms: object_uris = backend.get_accessible_objects(principals, perm) for object_uri in object_uris: perms_by_object_uri.setdefault(object_uri, []).append(perm) entries = [] for object_uri, perms in perms_by_object_uri.items(): # Obtain associated resource from object URI resource_name, matchdict = core_utils.view_lookup(request, object_uri) # For consistency with events payloads, prefix id with resource name matchdict[resource_name + '_id'] = matchdict.get('id') # Expand implicit permissions using descending tree. permissions = set(perms) for perm in perms: obtained = perms_descending_tree[resource_name][perm] # Related to same resource only and not every sub-objects. # (e.g "bucket:write" gives "bucket:read" but not "group:read") permissions |= obtained[resource_name] entry = dict(uri=object_uri, resource_name=resource_name, permissions=list(permissions), **matchdict) entries.append(entry) return {"data": entries}
def permissions_get(request): # Invert the permissions inheritance tree. perms_descending_tree = {} for obtained, obtained_from in PERMISSIONS_INHERITANCE_TREE.items(): on_resource, obtained_perm = obtained.split(':', 1) for from_resource, perms in obtained_from.items(): for perm in perms: perms_descending_tree.setdefault(from_resource, {})\ .setdefault(perm, {})\ .setdefault(on_resource, set())\ .add(obtained_perm) # Obtain current principals. principals = request.effective_principals if Authenticated in principals: # Since this view does not require any permission (can be used to # obtain public users permissions), we have to add the prefixed userid # among the principals (see :mode:`kinto.core.authentication`) userid = request.prefixed_userid principals.append(userid) # Query every possible permission of the current user from backend. backend = request.registry.permission perms_by_object_uri = backend.get_accessible_objects(principals) entries = [] for object_uri, perms in perms_by_object_uri.items(): try: # Obtain associated resource from object URI resource_name, matchdict = core_utils.view_lookup( request, object_uri) except ValueError: # Skip permissions entries that are not linked to an object URI. continue # For consistency with events payloads, prefix id with resource name matchdict[resource_name + '_id'] = matchdict.get('id') # Expand implicit permissions using descending tree. permissions = set(perms) for perm in perms: obtained = perms_descending_tree[resource_name][perm] # Related to same resource only and not every sub-objects. # (e.g "bucket:write" gives "bucket:read" but not "group:read") permissions |= obtained[resource_name] entry = dict(uri=object_uri, resource_name=resource_name, permissions=list(permissions), **matchdict) entries.append(entry) return {"data": entries}
def allowed_from_settings(settings, principals): """Returns every permissions allowed from settings for the current user. :param settings dict: app settings :param principals list: list of principals of current user :rtype: dict Result example:: { "bucket": {"write", "collection:create"}, "collection": {"read"} } XXX: This helper will be useful for Kinto/kinto#894 """ # Select settings about explicit permissions set on resources # bucket_write_principals = ... --> {("bucket", "write"): ["account:admin"]} perms_settings = { tuple(k.split("_", 3)[:2]): aslist(v) for k, v in settings.items() if re.match("(.+)_(create|write|read)_principals", k) } from_settings = {} for (resource_name, permission), allowed_principals in perms_settings.items(): # Keep the known permissions only. if resource_name not in PERMISSIONS_INHERITANCE_TREE.keys(): continue # Keep the permissions of the current user only. if not bool(set(principals) & set(allowed_principals)): continue # ``collection_create_principals`` means ``collection:create`` in bucket. if permission == "create": permission = f"{resource_name}:{permission}" resource_name = { # resource parents. "collection": "bucket", "group": "bucket", "record": "collection", "bucket": "root", "account": "root", }.get(resource_name, "") # Store them in a convenient way. from_settings.setdefault(resource_name, set()).add(permission) return from_settings
def allowed_from_settings(settings, principals): """Returns every permissions allowed from settings for the current user. :param settings dict: app settings :param principals list: list of principals of current user :rtype: dict Result example:: { "bucket": {"write", "collection:create"}, "collection": {"read"} } XXX: This helper will be useful for Kinto/kinto#894 """ perms_settings = { k: aslist(v) for k, v in settings.items() if k.endswith('_principals') } from_settings = {} for key, allowed_principals in perms_settings.items(): resource_name, permission, _ = key.split('_') # Keep the known permissions only. if resource_name not in PERMISSIONS_INHERITANCE_TREE.keys(): continue # Keep the permissions of the current user only. if not bool(set(principals) & set(allowed_principals)): continue # ``collection_create_principals`` means ``collection:create`` in bucket. if permission == 'create': permission = '{resource_name}:{permission}'.format( resource_name=resource_name, permission=permission) resource_name = { # resource parents. 'collection': 'bucket', 'group': 'bucket', 'record': 'collection', 'bucket': 'root', 'account': 'root', }.get(resource_name, '') # Store them in a convenient way. from_settings.setdefault(resource_name, set()).add(permission) return from_settings
def allowed_from_settings(settings, principals): """Returns every permissions allowed from settings for the current user. :param settings dict: app settings :param principals list: list of principals of current user :rtype: dict Result example:: { "bucket": {"write", "collection:create"}, "collection": {"read"} } XXX: This helper will be useful for Kinto/kinto#894 """ perms_settings = {k: aslist(v) for k, v in settings.items() if k.endswith('_principals')} from_settings = {} for key, allowed_principals in perms_settings.items(): resource_name, permission, _ = key.split('_') # Keep the known permissions only. if resource_name not in PERMISSIONS_INHERITANCE_TREE.keys(): continue # Keep the permissions of the current user only. if not bool(set(principals) & set(allowed_principals)): continue # ``collection_create_principals`` means ``collection:create`` in bucket. if permission == 'create': permission = '{resource_name}:{permission}'.format( resource_name=resource_name, permission=permission) resource_name = { # resource parents. 'collection': 'bucket', 'group': 'bucket', 'record': 'collection', 'bucket': 'root', 'account': 'root', }.get(resource_name, '') # Store them in a convenient way. from_settings.setdefault(resource_name, set()).add(permission) return from_settings
def get_records(self, filters=None, sorting=None, pagination_rules=None, limit=None, include_deleted=False, parent_id=None): # Invert the permissions inheritance tree. perms_descending_tree = {} for on_resource, tree in PERMISSIONS_INHERITANCE_TREE.items(): for obtained_perm, obtained_from in tree.items(): for from_resource, perms in obtained_from.items(): for perm in perms: perms_descending_tree.setdefault(from_resource, {})\ .setdefault(perm, {})\ .setdefault(on_resource, set())\ .add(obtained_perm) # Obtain current principals. principals = self.request.prefixed_principals # Query every possible permission of the current user from backend. backend = self.request.registry.permission perms_by_object_uri = backend.get_accessible_objects(principals) # Check settings for every allowed resources. from_settings = allowed_from_settings(self.request.registry.settings, principals) # Expand permissions obtained from backend with the object URIs that # correspond to permissions allowed from settings. allowed_resources = {'bucket', 'collection', 'group'} & set( from_settings.keys()) if allowed_resources: storage = self.request.registry.storage every_bucket, _ = storage.get_all(parent_id='', collection_id='bucket') for bucket in every_bucket: bucket_uri = '/buckets/{id}'.format(**bucket) for res in allowed_resources: resource_perms = from_settings[res] # Bucket is always fetched. if res == 'bucket': perms_by_object_uri.setdefault( bucket_uri, set()).update(resource_perms) continue # Fetch bucket collections and groups. # XXX: wrong approach: query in a loop! every_subobjects, _ = storage.get_all(parent_id=bucket_uri, collection_id=res) for subobject in every_subobjects: subobj_uri = bucket_uri + '/{0}s/{1}'.format( res, subobject['id']) perms_by_object_uri.setdefault( subobj_uri, set()).update(resource_perms) entries = [] for object_uri, perms in perms_by_object_uri.items(): try: # Obtain associated res from object URI resource_name, matchdict = core_utils.view_lookup( self.request, object_uri) except ValueError: # Skip permissions entries that are not linked to an object URI continue # For consistency with event payloads, prefix id with resource name matchdict[resource_name + '_id'] = matchdict.get('id') # Expand implicit permissions using descending tree. permissions = set(perms) for perm in perms: obtained = perms_descending_tree[resource_name][perm] # Related to same resource only and not every sub-objects. # (e.g "bucket:write" gives "bucket:read" but not "group:read") permissions |= obtained[resource_name] entry = dict(uri=object_uri, resource_name=resource_name, permissions=list(permissions), **matchdict) entries.append(entry) return extract_record_set(entries, filters=filters, sorting=sorting, pagination_rules=pagination_rules, limit=limit)
def get_records( self, filters=None, sorting=None, pagination_rules=None, limit=None, include_deleted=False, parent_id=None, ): # Invert the permissions inheritance tree. perms_descending_tree = {} for on_resource, tree in PERMISSIONS_INHERITANCE_TREE.items(): for obtained_perm, obtained_from in tree.items(): for from_resource, perms in obtained_from.items(): for perm in perms: perms_descending_tree.setdefault(from_resource, {}).setdefault( perm, {} ).setdefault(on_resource, set()).add(obtained_perm) # Obtain current principals. principals = self.request.prefixed_principals # Query every possible permission of the current user from backend. backend = self.request.registry.permission perms_by_object_uri = backend.get_accessible_objects(principals) # Check settings for every allowed resources. from_settings = allowed_from_settings(self.request.registry.settings, principals) # Add additional resources and permissions defined in settings/plugins for root_perm in from_settings.get("root", []): resource_name, _ = root_perm.split(":") perms_by_object_uri.setdefault("/", set()).add(root_perm) perms_descending_tree.setdefault("root", {}).update({root_perm: {"root": {root_perm}}}) # Expand permissions obtained from backend with the object URIs that # correspond to permissions allowed from settings. allowed_resources = {"bucket", "collection", "group"} & set(from_settings.keys()) if allowed_resources: storage = self.request.registry.storage every_bucket, _ = storage.get_all(parent_id="", collection_id="bucket") for bucket in every_bucket: bucket_uri = "/buckets/{id}".format_map(bucket) for res in allowed_resources: resource_perms = from_settings[res] # Bucket is always fetched. if res == "bucket": perms_by_object_uri.setdefault(bucket_uri, set()).update(resource_perms) continue # Fetch bucket collections and groups. # XXX: wrong approach: query in a loop! every_subobjects, _ = storage.get_all(parent_id=bucket_uri, collection_id=res) for subobject in every_subobjects: subobj_uri = bucket_uri + "/{0}s/{1}".format(res, subobject["id"]) perms_by_object_uri.setdefault(subobj_uri, set()).update(resource_perms) entries = [] for object_uri, perms in perms_by_object_uri.items(): try: # Obtain associated res from object URI resource_name, matchdict = core_utils.view_lookup(self.request, object_uri) except ValueError: # Skip permissions entries that are not linked to an object URI continue # For consistency with event payloads, if resource has an id, # prefix it with its resource name if "id" in matchdict: matchdict[resource_name + "_id"] = matchdict["id"] # The imaginary "root" resource gets mapped to the hello # view. Handle it explicitly. if resource_name == "hello": resource_name = "root" # Expand implicit permissions using descending tree. permissions = set(perms) for perm in perms: obtained = perms_descending_tree[resource_name][perm] # Related to same resource only and not every sub-objects. # (e.g "bucket:write" gives "bucket:read" but not "group:read") permissions |= obtained[resource_name] entry = dict( uri=object_uri, resource_name=resource_name, permissions=list(permissions), **matchdict ) entries.append(entry) return extract_record_set( entries, filters=filters, sorting=sorting, id_field="uri", pagination_rules=pagination_rules, limit=limit, )
def get_objects( self, filters=None, sorting=None, pagination_rules=None, limit=None, include_deleted=False, parent_id=None, ): # Invert the permissions inheritance tree. perms_descending_tree = {} for on_resource, tree in PERMISSIONS_INHERITANCE_TREE.items(): for obtained_perm, obtained_from in tree.items(): for from_resource, perms in obtained_from.items(): for perm in perms: perms_descending_tree.setdefault( from_resource, {}).setdefault(perm, {}).setdefault( on_resource, set()).add(obtained_perm) # Obtain current principals. principals = self.request.prefixed_principals # Query every possible permission of the current user from backend. backend = self.request.registry.permission perms_by_object_uri = backend.get_accessible_objects(principals) # Check settings for every allowed resources. from_settings = allowed_from_settings(self.request.registry.settings, principals) # Add additional resources and permissions defined in settings/plugins for root_perm in from_settings.get("root", []): resource_name, _ = root_perm.split(":") perms_by_object_uri.setdefault("/", set()).add(root_perm) perms_descending_tree.setdefault("root", {}).update( {root_perm: { "root": {root_perm} }}) # Expand permissions obtained from backend with the object URIs that # correspond to permissions allowed from settings. allowed_resources = {"bucket", "collection", "group"} & set( from_settings.keys()) if allowed_resources: storage = self.request.registry.storage every_bucket, _ = storage.get_all(parent_id="", resource_name="bucket") for bucket in every_bucket: bucket_uri = "/buckets/{id}".format_map(bucket) for res in allowed_resources: resource_perms = from_settings[res] # Bucket is always fetched. if res == "bucket": perms_by_object_uri.setdefault( bucket_uri, set()).update(resource_perms) continue # Fetch bucket collections and groups. # XXX: wrong approach: query in a loop! every_subobjects, _ = storage.get_all(parent_id=bucket_uri, resource_name=res) for subobject in every_subobjects: subobj_uri = bucket_uri + f"/{res}s/{subobject['id']}" perms_by_object_uri.setdefault( subobj_uri, set()).update(resource_perms) entries = [] for object_uri, perms in perms_by_object_uri.items(): try: # Obtain associated res from object URI resource_name, matchdict = core_utils.view_lookup( self.request, object_uri) except ValueError: # Skip permissions entries that are not linked to an object URI continue # For consistency with event payloads, if resource has an id, # prefix it with its resource name if "id" in matchdict: matchdict[resource_name + "_id"] = matchdict["id"] # The imaginary "root" resource gets mapped to the hello # view. Handle it explicitly. if resource_name == "hello": resource_name = "root" # Expand implicit permissions using descending tree. permissions = set(perms) for perm in perms: obtained = perms_descending_tree[resource_name][perm] # Related to same resource only and not every sub-objects. # (e.g "bucket:write" gives "bucket:read" but not "group:read") permissions |= obtained[resource_name] entry = dict( uri=object_uri, resource_name=resource_name, permissions=list(permissions), **matchdict, ) entries.append(entry) return extract_object_set( entries, filters=filters, sorting=sorting, id_field="uri", pagination_rules=pagination_rules, limit=limit, )