def test_permission_grants_for_group(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group("some-permission", "one", "some-group") setup.grant_permission_to_group("some-permission", "two", "some-group") setup.grant_permission_to_group("other-permission", "*", "some-group") setup.grant_permission_to_group("parent-permission", "foo", "parent-group") setup.add_group_to_group("some-group", "parent-group") setup.create_group("other-group") setup.create_user("*****@*****.**") mock_ui = MagicMock() usecase = setup.usecase_factory.create_grant_permission_to_service_account_usecase( "*****@*****.**", mock_ui) expected = [ GroupPermissionGrant( group="some-group", permission="other-permission", argument="*", granted_on=ANY, is_alias=False, grant_id=ANY, ), GroupPermissionGrant( group="some-group", permission="parent-permission", argument="foo", granted_on=ANY, is_alias=False, grant_id=ANY, ), GroupPermissionGrant( group="some-group", permission="some-permission", argument="one", granted_on=ANY, is_alias=False, grant_id=ANY, ), GroupPermissionGrant( group="some-group", permission="some-permission", argument="two", granted_on=ANY, is_alias=False, grant_id=ANY, ), ] assert sorted( usecase.permission_grants_for_group("some-group")) == expected assert usecase.permission_grants_for_group("other-group") == []
def group_grants_for_permission(self, name, include_disabled_groups=False, argument=None): # type: (str, bool, Optional[str]) -> List[GroupPermissionGrant] permission = Permission.get(self.session, name=name) if not permission or not permission.enabled: return [] grants = (self.session.query( Group.groupname, PermissionMap.argument, PermissionMap.id, PermissionMap.granted_on).filter( PermissionMap.permission_id == permission.id, Group.id == PermissionMap.group_id).order_by( Group.groupname, PermissionMap.argument)) if not include_disabled_groups: grants = grants.filter(Group.enabled == True) if argument: grants = grants.filter(PermissionMap.argument == argument) return [ GroupPermissionGrant( group=g.groupname, permission=name, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in grants.all() ]
def test_permission_disable_existing_grants(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group(PERMISSION_ADMIN, "", "admins") setup.add_user_to_group("*****@*****.**", "admins") setup.grant_permission_to_group("some-permission", "argument", "some-group") setup.create_service_account("*****@*****.**", "some-group") setup.grant_permission_to_service_account("some-permission", "", "*****@*****.**") mock_ui = MagicMock() usecase = setup.usecase_factory.create_disable_permission_usecase( "*****@*****.**", mock_ui) usecase.disable_permission("some-permission") assert mock_ui.mock_calls == [ call.disable_permission_failed_existing_grants( "some-permission", [ GroupPermissionGrant("some-group", "some-permission", "argument") ], [ ServiceAccountPermissionGrant("*****@*****.**", "some-permission", "") ], ) ]
def test_grant_permission(tmpdir: LocalPath, setup: SetupTest, browser: Chrome) -> None: with setup.transaction(): setup.add_user_to_group("*****@*****.**", "some-group") setup.grant_permission_to_group(PERMISSION_GRANT, "some-permission", "some-group") setup.create_permission("some-permission") setup.create_group("other-group") with frontend_server(tmpdir, "*****@*****.**") as frontend_url: browser.get(url(frontend_url, "/groups/some-group")) group_page = GroupViewPage(browser) assert group_page.find_permission_rows("some-permission") == [] group_page.click_add_permission_button() grant_page = PermissionGrantPage(browser) grant_page.set_permission("some-permission") grant_page.set_argument("foo") grant_page.submit() rows = group_page.find_permission_rows("some-permission") assert len(rows) == 1 assert rows[0].argument == "foo" # Grant a permission with surrounding and internal whitespace to test whitespace handling. browser.get(url(frontend_url, "/groups/other-group")) assert group_page.find_permission_rows("some-permission") == [] group_page.click_add_permission_button() grant_page.set_permission("some-permission") grant_page.set_argument(" arg u ment ") grant_page.submit() rows = group_page.find_permission_rows("some-permission") assert len(rows) == 1 assert rows[0].argument in ("arg u ment", "arg u ment" ) # browser messes with whitespace # Check directly in the database to make sure the whitespace is stripped, since we may not be # able to see it via the browser. We need to explicitly reopen the database since otherwise # SQLite doesn't always see changes written by the frontend. setup.reopen_database() permission_grant_repository = setup.sql_repository_factory.create_permission_grant_repository( ) grants = permission_grant_repository.permission_grants_for_group( "other-group") assert grants == [ GroupPermissionGrant( group="other-group", permission="some-permission", argument="arg u ment", granted_on=ANY, is_alias=False, grant_id=ANY, ) ]
def _permission_grants_for_group_ids(self, group_ids): # type: (Iterable[int]) -> List[GroupPermissionGrant] """Given a set of group IDs, return all direct or inherited permission grants. Used to build the full list of permission grants for a set of groups, taking inheritance into account. Shared code between permission_grants_for_user and permission_grants_for_group. TODO(rra): Currently does not expand permission aliases, and therefore doesn't match the graph behavior. """ # Get the parent groups of the initial groups, repeating until we run out of levels of the # tree. Use a set of seen group_ids to avoid querying the same group twice if a user is a # member of it via multiple paths. now = datetime.utcnow() seen_group_ids = set(group_ids) while group_ids: parent_groups = (self.session.query(Group.id).join( GroupEdge, Group.id == GroupEdge.group_id).filter( GroupEdge.member_pk.in_(group_ids), Group.enabled == True, GroupEdge.active == True, GroupEdge.member_type == OBJ_TYPES["Group"], GroupEdge._role != GROUP_EDGE_ROLES.index("np-owner"), or_(GroupEdge.expiration > now, GroupEdge.expiration == None), ).distinct()) group_ids = [ g.id for g in parent_groups if g.id not in seen_group_ids ] seen_group_ids.update(group_ids) # Return the permission grants. group_permission_grants = (self.session.query( Group.groupname, Permission.name, PermissionMap.argument, PermissionMap.granted_on, PermissionMap.id, ).filter( Permission.id == PermissionMap.permission_id, PermissionMap.group_id.in_(seen_group_ids), Group.id == PermissionMap.group_id, ).all()) return [ GroupPermissionGrant( group=g.groupname, permission=g.name, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in group_permission_grants ]
def _get_group_grants(session): # type: (Session) -> Dict[str, List[GroupPermissionGrant]] """Returns a dict of group names to lists of permission grants.""" permissions = session.query(SQLPermission, PermissionMap, SQLGroup.groupname).filter( SQLPermission.id == PermissionMap.permission_id, PermissionMap.group_id == SQLGroup.id, SQLGroup.enabled == True, ) out = defaultdict(list) # type: Dict[str, List[GroupPermissionGrant]] for (permission, permission_map, groupname) in permissions: out[groupname].append( GroupPermissionGrant( group=groupname, permission=permission.name, argument=permission_map.argument, granted_on=permission_map.granted_on, is_alias=False, grant_id=permission_map.id, ) ) aliases = get_plugin_proxy().get_aliases_for_mapped_permission( session, permission.name, permission_map.argument ) for (name, arg) in aliases: out[groupname].append( GroupPermissionGrant( group=groupname, permission=name, argument=arg, granted_on=permission_map.granted_on, is_alias=True, grant_id=None, ) ) return out
def group_grants_for_permission(self, name, include_disabled_groups=False): # type: (str, bool) -> List[GroupPermissionGrant] permission = Permission.get(self.session, name=name) if not permission or not permission.enabled: return [] grants = ( self.session.query(Group.groupname, PermissionMap.argument) .filter( PermissionMap.permission_id == permission.id, Group.id == PermissionMap.group_id ) .order_by(Group.groupname, PermissionMap.argument) ) if not include_disabled_groups: grants = grants.filter(Group.enabled == True) return [GroupPermissionGrant(g.groupname, name, g.argument) for g in grants.all()]
def permission_grants_for_user(self, name): # type: (str) -> List[GroupPermissionGrant] user_details = self.graph.get_user_details(name) permissions = [] for permission_data in user_details["permissions"]: permission = GroupPermissionGrant( group=permission_data["path"][-1], permission=permission_data["permission"], argument=permission_data["argument"], granted_on=datetime.utcfromtimestamp( permission_data["granted_on"]), is_alias=permission_data["alias"], ) permissions.append(permission) return permissions
def revoke_all_group_grants(self, permission): # type: (str) -> List[GroupPermissionGrant] sql_permission = Permission.get(self.session, name=permission) if not sql_permission: return [] grants = ( self.session.query(PermissionMap.id, Group.groupname, PermissionMap.argument) .filter( Group.id == PermissionMap.group_id, PermissionMap.permission_id == sql_permission.id, ) .all() ) ids = [g.id for g in grants] self.session.query(PermissionMap).filter(PermissionMap.id.in_(ids)).delete( synchronize_session="fetch" ) return [GroupPermissionGrant(g.groupname, permission, g.argument) for g in grants]
def test_end_to_end_whitespace_in_argument(tmpdir, setup, browser): # type: (LocalPath, SetupTest, Chrome) -> None with setup.transaction(): setup.create_group("some-group") setup.create_permission("some-permission") setup.add_user_to_group("*****@*****.**", "some-group", "owner") setup.add_user_to_group("*****@*****.**", "admins") setup.grant_permission_to_group(PERMISSION_GRANT, "some-permission", "admins") with frontend_server(tmpdir, "*****@*****.**") as frontend_url: browser.get(url(frontend_url, "/permissions/some-permission")) permission_page = PermissionPage(browser) assert permission_page.subheading == "some-permission" permission_page.button_to_request_this_permission.click() permission_request_page = PermissionRequestPage(browser) permission_request_page.set_group("some-group") permission_request_page.set_argument_freeform(" arg u ment ") permission_request_page.set_reason("testing whitespace") permission_request_page.submit() with frontend_server(tmpdir, "*****@*****.**") as frontend_url: browser.get(url(frontend_url, "/permissions/requests?status=pending")) requests_page = PermissionRequestsPage(browser) request_rows = requests_page.request_rows assert len(request_rows) == 1 request = request_rows[0] request.click_modify_link() modify_page = PermissionRequestUpdatePage(browser) modify_page.set_status("actioned") modify_page.set_reason("testing whitespace") modify_page.submit() browser.get(url(frontend_url, "/groups/some-group?refresh=yes")) group_page = GroupViewPage(browser) permission_rows = group_page.find_permission_rows("some-permission") assert len(permission_rows) == 1 grant = permission_rows[0] assert grant.name == "some-permission" assert grant.argument in ("arg u ment", "arg u ment") # browser messes with whitespace assert grant.source == "(direct)" # Check directly in the database to make sure the whitespace is stripped, since we may not be # able to see it via the browser. We need to explicitly reopen the database since otherwise # SQLite doesn't always see changes written by the frontend. setup.reopen_database() permission_grant_repository = setup.sql_repository_factory.create_permission_grant_repository() grants = permission_grant_repository.permission_grants_for_group("some-group") assert grants == [ GroupPermissionGrant( group="some-group", permission="some-permission", argument="arg u ment", granted_on=ANY, is_alias=False, grant_id=ANY, ) ]
def permission_grants_for_user(self, name): # type: (str) -> List[GroupPermissionGrant] """Return all permission grants a user has from whatever source. TODO(rra): Currently does not expand permission aliases, and therefore doesn't match the graph behavior. Use with caution until that is fixed. """ now = datetime.utcnow() user = User.get(self.session, name=name) if not user or user.role_user or user.is_service_account or not user.enabled: return [] # Get the groups of which this user is a direct member. groups = (self.session.query(Group.id).join( GroupEdge, Group.id == GroupEdge.group_id).join( User, User.id == GroupEdge.member_pk).filter( Group.enabled == True, User.id == user.id, GroupEdge.active == True, GroupEdge.member_type == OBJ_TYPES["User"], GroupEdge._role != GROUP_EDGE_ROLES.index("np-owner"), or_(GroupEdge.expiration > now, GroupEdge.expiration == None), ).distinct()) group_ids = [g.id for g in groups] # If the user was not a member of any group, we can return early. if not group_ids: return [] # Now, get the parent groups of those groups and so forth until we run out of levels of the # tree. Use a set of seen group_ids to avoid querying the same group twice if a user is a # member of it via multiple paths. seen_group_ids = set(group_ids) while group_ids: parent_groups = (self.session.query(Group.id).join( GroupEdge, Group.id == GroupEdge.group_id).filter( GroupEdge.member_pk.in_(group_ids), Group.enabled == True, GroupEdge.active == True, GroupEdge.member_type == OBJ_TYPES["Group"], GroupEdge._role != GROUP_EDGE_ROLES.index("np-owner"), or_(GroupEdge.expiration > now, GroupEdge.expiration == None), ).distinct()) group_ids = [ g.id for g in parent_groups if g.id not in seen_group_ids ] seen_group_ids.update(group_ids) # Return the permission grants. group_permission_grants = (self.session.query( Group.groupname, Permission.name, PermissionMap.argument, PermissionMap.granted_on, PermissionMap.id, ).filter( Permission.id == PermissionMap.permission_id, PermissionMap.group_id.in_(seen_group_ids), Group.id == PermissionMap.group_id, ).all()) return [ GroupPermissionGrant( group=g.groupname, permission=g.name, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in group_permission_grants ]