def user_grantable_permissions(session, user): ''' Returns a list of permissions this user is allowed to grant. Presently, this only counts permissions that a user has directly -- in other words, the 'grant' permissions are not counted as inheritable. TODO: consider making these permissions inherited? This requires walking the graph, which is expensive. Returns a list of tuples (Permission, argument) that the user is allowed to grant. ''' # avoid circular dependency from grouper.permissions import filter_grantable_permissions all_permissions = { permission.name: permission for permission in Permission.get_all(session) } if user_is_permission_admin(session, user): result = [(perm, '*') for perm in all_permissions.values()] return sorted(result, key=lambda x: x[0].name + x[1]) # Someone can grant a permission if they are a member of a group that has a permission # of PERMISSION_GRANT with an argument that matches the name of a permission. grants = [ x for x in user_permissions(session, user) if x.name == PERMISSION_GRANT ] return filter_grantable_permissions(session, grants)
def filter_grantable_permissions(session, grants, all_permissions=None): """For a given set of PERMISSION_GRANT permissions, return all permissions that are grantable. Args: session (sqlalchemy.orm.session.Session); database session grants ([Permission, ...]): PERMISSION_GRANT permissions all_permissions ({name: Permission}): all permissions to check against Returns: list of (Permission, argument) that is grantable by list of grants sorted by permission name and argument. """ if all_permissions is None: all_permissions = {permission.name: permission for permission in Permission.get_all(session)} result = [] for grant in grants: assert grant.name == PERMISSION_GRANT grantable = grant.argument.split('/', 1) if not grantable: continue for name, permission_obj in all_permissions.iteritems(): if matches_glob(grantable[0], name): result.append((permission_obj, grantable[1] if len(grantable) > 1 else '*', )) return sorted(result, key=lambda x: x[0].name + x[1])
def get_owners_by_grantable_permission(session, separate_global=False): """ Returns all known permission arguments with owners. This consolidates permission grants supported by grouper itself as well as any grants governed by plugins. Args: session(sqlalchemy.orm.session.Session): database session Returns: A map of permission to argument to owners of the form {permission: {argument: [owner1, ...], }, } where 'owners' are models.Group objects. And 'argument' can be '*' which means 'anything'. """ all_permissions = {permission.name: permission for permission in Permission.get_all(session)} all_groups = session.query(Group).filter(Group.enabled == True).all() owners_by_arg_by_perm = defaultdict(lambda: defaultdict(list)) all_group_permissions = session.query( Permission.name, PermissionMap.argument, PermissionMap.granted_on, Group, ).filter( PermissionMap.group_id == Group.id, Permission.id == PermissionMap.permission_id, ).all() grants_by_group = defaultdict(list) for grant in all_group_permissions: grants_by_group[grant.Group.id].append(grant) for group in all_groups: # special case permission admins group_permissions = grants_by_group[group.id] if any(filter(lambda g: g.name == PERMISSION_ADMIN, group_permissions)): for perm_name in all_permissions: owners_by_arg_by_perm[perm_name]["*"].append(group) if separate_global: owners_by_arg_by_perm[GLOBAL_OWNERS]["*"].append(group) continue grants = [gp for gp in group_permissions if gp.name == PERMISSION_GRANT] for perm, arg in filter_grantable_permissions(session, grants, all_permissions=all_permissions): owners_by_arg_by_perm[perm.name][arg].append(group) # merge in plugin results for plugin in get_plugins(): res = plugin.get_owner_by_arg_by_perm(session) or {} for perm, owners_by_arg in res.items(): for arg, owners in owners_by_arg.items(): owners_by_arg_by_perm[perm][arg] += owners return owners_by_arg_by_perm
def get_owners_by_grantable_permission(session, separate_global=False): """ Returns all known permission arguments with owners. This consolidates permission grants supported by grouper itself as well as any grants governed by plugins. Args: session(sqlalchemy.orm.session.Session): database session Returns: A map of permission to argument to owners of the form {permission: {argument: [owner1, ...], }, } where 'owners' are models.Group objects. And 'argument' can be '*' which means 'anything'. """ all_permissions = {permission.name: permission for permission in Permission.get_all(session)} all_groups = session.query(Group).filter(Group.enabled == True).all() owners_by_arg_by_perm = defaultdict(lambda: defaultdict(list)) all_group_permissions = session.query( Permission.name, PermissionMap.argument, PermissionMap.granted_on, Group, ).filter( PermissionMap.group_id == Group.id, Permission.id == PermissionMap.permission_id, ).all() grants_by_group = defaultdict(list) for grant in all_group_permissions: grants_by_group[grant.Group.id].append(grant) for group in all_groups: # special case permission admins group_permissions = grants_by_group[group.id] if any(filter(lambda g: g.name == PERMISSION_ADMIN, group_permissions)): for perm_name in all_permissions: owners_by_arg_by_perm[perm_name]["*"].append(group) if separate_global: owners_by_arg_by_perm[GLOBAL_OWNERS]["*"].append(group) continue grants = [gp for gp in group_permissions if gp.name == PERMISSION_GRANT] for perm, arg in filter_grantable_permissions(session, grants, all_permissions=all_permissions): owners_by_arg_by_perm[perm.name][arg].append(group) # merge in plugin results for res in get_plugin_proxy().get_owner_by_arg_by_perm(session): for perm, owners_by_arg in res.items(): for arg, owners in owners_by_arg.items(): owners_by_arg_by_perm[perm][arg] += owners return owners_by_arg_by_perm
def test_permission_grant_to_owners(session, standard_graph, groups, grantable_permissions): """Test we're getting correct owners according to granted 'grouper.permission.grant' permissions.""" perm_grant, _, perm1, perm2 = grantable_permissions assert not get_owners_by_grantable_permission(session), "nothing to begin with" # grant a grant on a non-existent permission grant_permission(groups["auditors"], perm_grant, argument="notgrantable.one") assert not get_owners_by_grantable_permission(session), "ignore grants for non-existent perms" # grant a wildcard grant -- make sure all permissions are represented and # the grant isn't inherited grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups["all-teams"]] assert owners_by_arg_by_perm[perm1.name]["*"] == expected, "grants are not inherited" assert len(owners_by_arg_by_perm) == 2 assert len(owners_by_arg_by_perm[perm1.name]) == 1 assert len(owners_by_arg_by_perm[perm2.name]) == 1 # grant on argument substring grant_permission(groups["team-sre"], perm_grant, argument="{}/somesubstring*".format(perm1.name)) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups["all-teams"]] assert owners_by_arg_by_perm[perm1.name]["*"] == expected expected = [groups["team-sre"]] assert owners_by_arg_by_perm[perm1.name]["somesubstring*"] == expected # make sure get_owner() respect substrings res = [ o for o, a in get_owner_arg_list(session, perm1, "somesubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert ( sorted(res) == sorted([groups["all-teams"], groups["team-sre"]]), "should include substring wildcard matches", ) res = [ o for o, a in get_owner_arg_list(session, perm1, "othersubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == [groups["all-teams"]], "negative test of substring wildcard matches" # permission admins have all the power perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) all_permissions = Permission.get_all(session) for perm in all_permissions: assert perm.name in owners_by_arg_by_perm, "all permission should be represented" assert ( groups["security-team"] in owners_by_arg_by_perm[perm.name]["*"] ), "permission admin should be wildcard owners"
def user_grantable_permissions(session, user): ''' Returns a list of permissions this user is allowed to grant. Presently, this only counts permissions that a user has directly -- in other words, the 'grant' permissions are not counted as inheritable. TODO: consider making these permissions inherited? This requires walking the graph, which is expensive. Returns a list of tuples (Permission, argument) that the user is allowed to grant. ''' # avoid circular dependency from grouper.permissions import filter_grantable_permissions all_permissions = {permission.name: permission for permission in Permission.get_all(session)} if user_is_permission_admin(session, user): result = [(perm, '*') for perm in all_permissions.values()] return sorted(result, key=lambda x: x[0].name + x[1]) # Someone can grant a permission if they are a member of a group that has a permission # of PERMISSION_GRANT with an argument that matches the name of a permission. grants = [x for x in user_permissions(session, user) if x.name == PERMISSION_GRANT] return filter_grantable_permissions(session, grants)
def test_permission_grant_to_owners(session, standard_graph, groups, grantable_permissions): """Test we're getting correct owners according to granted 'grouper.permission.grant' permissions.""" perm_grant, _, perm1, perm2 = grantable_permissions assert not get_owners_by_grantable_permission( session), 'nothing to begin with' # grant a grant on a non-existent permission grant_permission(groups["auditors"], perm_grant, argument="notgrantable.one") assert not get_owners_by_grantable_permission( session), 'ignore grants for non-existent perms' # grant a wildcard grant -- make sure all permissions are represented and # the grant isn't inherited grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups['all-teams']] assert owners_by_arg_by_perm[ perm1.name]['*'] == expected, 'grants are not inherited' assert len(owners_by_arg_by_perm) == 2 assert len(owners_by_arg_by_perm[perm1.name]) == 1 assert len(owners_by_arg_by_perm[perm2.name]) == 1 # grant on argument substring grant_permission(groups["team-sre"], perm_grant, argument="{}/somesubstring*".format(perm1.name)) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups['all-teams']] assert owners_by_arg_by_perm[perm1.name]['*'] == expected expected = [groups["team-sre"]] assert owners_by_arg_by_perm[perm1.name]['somesubstring*'] == expected # make sure get_owner() respect substrings res = [ o for o, a in get_owner_arg_list( session, perm1, "somesubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == sorted([groups["all-teams"], groups["team-sre"]]), \ "should include substring wildcard matches" res = [ o for o, a in get_owner_arg_list( session, perm1, "othersubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == [groups["all-teams"] ], "negative test of substring wildcard matches" # permission admins have all the power perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) all_permissions = Permission.get_all(session) for perm in all_permissions: assert perm.name in owners_by_arg_by_perm, 'all permission should be represented' assert groups["security-team"] in owners_by_arg_by_perm[perm.name]["*"], \ 'permission admin should be wildcard owners'