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, 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) 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 revoke_all_service_account_grants(self, permission): # type: (str) -> List[ServiceAccountPermissionGrant] sql_permission = Permission.get(self.session, name=permission) if not sql_permission: return [] grants = ( self.session.query( ServiceAccountPermissionMap.id, User.username, ServiceAccountPermissionMap.argument, ServiceAccountPermissionMap.granted_on, ) .filter( User.id == ServiceAccount.user_id, ServiceAccount.id == ServiceAccountPermissionMap.service_account_id, PermissionMap.permission_id == sql_permission.id, ) .all() ) ids = [g.id for g in grants] self.session.query(ServiceAccountPermissionMap).filter( ServiceAccountPermissionMap.id.in_(ids) ).delete(synchronize_session="fetch") return [ ServiceAccountPermissionGrant( service_account=g.username, permission=permission, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in grants ]
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, PermissionMap.granted_on ) .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( group=g.groupname, permission=permission, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in grants ]
def test_permission_exclude_inactive(session, standard_graph): """Ensure disabled groups are excluded from permission data.""" group = Group.get(session, name="team-sre") permission = Permission.get(session, name="ssh") assert "team-sre" in [g[0] for g in get_groups_by_permission(session, permission)] group.disable() assert "team-sre" not in [g[0] for g in get_groups_by_permission(session, permission)]
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 revoke_all_service_account_grants(self, permission): # type: (str) -> List[ServiceAccountPermissionGrant] sql_permission = Permission.get(self.session, name=permission) if not sql_permission: return [] grants = (self.session.query( ServiceAccountPermissionMap.id, User.username, ServiceAccountPermissionMap.argument, ServiceAccountPermissionMap.granted_on, ).filter( User.id == ServiceAccount.user_id, ServiceAccount.id == ServiceAccountPermissionMap.service_account_id, PermissionMap.permission_id == sql_permission.id, ).all()) ids = [g.id for g in grants] self.session.query(ServiceAccountPermissionMap).filter( ServiceAccountPermissionMap.id.in_(ids)).delete( synchronize_session="fetch") return [ ServiceAccountPermissionGrant( service_account=g.username, permission=permission, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in grants ]
def service_account_grants_for_permission(self, name): # type: (str) -> List[ServiceAccountPermissionGrant] permission = Permission.get(self.session, name=name) if not permission or not permission.enabled: return [] grants = (self.session.query( User.username, ServiceAccountPermissionMap.argument, ServiceAccountPermissionMap.granted_on, ServiceAccountPermissionMap.id, ).filter( ServiceAccountPermissionMap.permission_id == permission.id, ServiceAccount.id == ServiceAccountPermissionMap.service_account_id, User.id == ServiceAccount.user_id, ).order_by(User.username, ServiceAccountPermissionMap.argument)) return [ ServiceAccountPermissionGrant( service_account=g.username, permission=name, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in grants.all() ]
def service_account_grants_for_permission(self, name): # type: (str) -> List[ServiceAccountPermissionGrant] permission = Permission.get(self.session, name=name) if not permission or not permission.enabled: return [] grants = ( self.session.query( User.username, ServiceAccountPermissionMap.argument, ServiceAccountPermissionMap.granted_on, ServiceAccountPermissionMap.id, ) .filter( ServiceAccountPermissionMap.permission_id == permission.id, ServiceAccount.id == ServiceAccountPermissionMap.service_account_id, User.id == ServiceAccount.user_id, ) .order_by(User.username, ServiceAccountPermissionMap.argument) ) return [ ServiceAccountPermissionGrant( service_account=g.username, permission=name, argument=g.argument, granted_on=g.granted_on, is_alias=False, grant_id=g.id, ) for g in grants.all() ]
def sync_db_command(args): # Models not implicitly or explictly imported above are explicitly imported # here: from grouper.models.perf_profile import PerfProfile # noqa db_engine = get_db_engine(get_database_url(settings)) Model.metadata.create_all(db_engine) # Add some basic database structures we know we will need if they don't exist. session = make_session() for name, description in SYSTEM_PERMISSIONS: test = Permission.get(session, name) if test: continue permission = Permission(name=name, description=description) try: permission.add(session) session.flush() except IntegrityError: session.rollback() raise Exception('Failed to create permission: %s' % (name, )) session.commit() # This group is needed to bootstrap a Grouper installation. admin_group = Group.get(session, name="grouper-administrators") if not admin_group: admin_group = Group( groupname="grouper-administrators", description="Administrators of the Grouper system.", canjoin="nobody", ) try: admin_group.add(session) session.flush() except IntegrityError: session.rollback() raise Exception('Failed to create group: grouper-administrators') for permission_name in (GROUP_ADMIN, PERMISSION_ADMIN, USER_ADMIN): permission = Permission.get(session, permission_name) assert permission, "Permission should have been created earlier!" grant_permission(session, admin_group.id, permission.id) session.commit()
def test_grant_and_revoke(session, standard_graph, graph, groups, permissions, http_client, base_url): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): return any(map(lambda x: x.permission == permission_name, graph.permission_metadata[group_name])) # make some permission admins perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) # grant attempt by non-permission admin fe_url = url(base_url, "/permissions/grant/{}".format(group_name)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission": permission_name, "argument": "specific_arg"}), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert not _check_graph_for_perm(graph), "no permissions granted" # grant by permission admin resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission": permission_name, "argument": "specific_arg"}), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions granted, successfully" # figure out mapping_id of grant permission_id = Permission.get(session, name=permission_name).id group_id = Group.get(session, name=group_name).id mapping = session.query(PermissionMap).filter( PermissionMap.permission_id == permission_id, PermissionMap.group_id == group_id).first() # revoke permission by non-admin fe_url = url(base_url, "/permissions/{}/revoke/{}".format(permission_name, mapping.id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions not revoked" # revoke permission for realz resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert not _check_graph_for_perm(graph), "permissions revoked successfully"
def create_permission_requests(setup: SetupTest) -> None: """Create a permission requesting scenario. Set up a permission requesting scenario in which [email protected] has both inbound and outbound requests that they should be able to see on the requests page. """ with setup.transaction(): setup.create_permission("perm.hasgranter", description="perm with granter") setup.create_permission("perm.nogranter", description="perm without granter") setup.add_user_to_group("*****@*****.**", "auditors") setup.grant_permission_to_group(PERMISSION_GRANT, "perm.hasgranter/a", "auditors") setup.add_user_to_group("*****@*****.**", "group-admins") setup.grant_permission_to_group(PERMISSION_ADMIN, "", "group-admins") # The old API requires SQLAlchemy objects. granting_user = User.get(setup.session, name="*****@*****.**") assert granting_user granting_group = Group.get(setup.session, name="auditors") assert granting_group requesting_user = User.get(setup.session, name="*****@*****.**") assert requesting_user requesting_group = Group.get(setup.session, name="group-admins") assert requesting_group perm_granter = Permission.get(setup.session, "perm.hasgranter") assert perm_granter perm_nogranter = Permission.get(setup.session, "perm.nogranter") assert perm_nogranter perm_admin = Permission.get(setup.session, PERMISSION_ADMIN) assert perm_admin # The old APIs require a global settings object. set_global_settings(setup.settings) # Request the two test perms from group-admins. with setup.transaction(): create_request( setup.session, requesting_user, requesting_group, perm_granter, "a", "reasons" ) create_request( setup.session, requesting_user, requesting_group, perm_nogranter, "a", "reasons" ) # Finally make one more request from a user other than [email protected]. with setup.transaction(): create_request(setup.session, granting_user, granting_group, perm_admin, "a", "reasons")
def test_revoke_permission_from_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") fe_url = url(base_url, '/tags') resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ 'tagname': "tyler_was_here", "description": "Test Tag Please Ignore" }), headers={'X-Grouper-User': user.username}) tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/permissions/grant_tag/{}'.format(tag.name)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ 'permission': TAG_EDIT, "argument": "*" }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") perm = Permission.get(session, TAG_EDIT) assert len(get_public_key_tag_permissions( session, tag)) == 1, "The tag should have exactly 1 permission" user = session.query(User).filter_by(username="******").scalar() mapping = get_public_key_tag_permissions(session, tag)[0] fe_url = url( base_url, '/permissions/{}/revoke_tag/{}'.format(TAG_EDIT, mapping.mapping_id)) resp = yield http_client.fetch(fe_url, method="POST", body="", headers={'X-Grouper-User': user.username}) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") assert len(get_public_key_tag_permissions( session, tag)) == 0, "The tag should have no permissions"
def entries_affecting_permission(self, permission, limit): # type: (str, int) -> List[AuditLogEntry] permission_obj = Permission.get(self.session, name=permission) if not permission_obj: return [] results = (self.session.query(AuditLog).filter( AuditLog.on_permission_id == permission_obj.id).order_by( desc(AuditLog.log_time)).limit(limit)) return [self._to_audit_log_entry(e) for e in results]
def get_permission(self, name): # type: (str) -> Optional[Permission] permission = SQLPermission.get(self.session, name=name) if not permission: return None return Permission( name=permission.name, description=permission.description, created_on=permission.created_on, )
def post(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() grantable = group.my_permissions() form = self.get_form(grantable) if not form.validate(): return self.render( "service-account-permission-grant.html", form=form, user=user, group=group, alerts=self.get_form_alerts(form.errors) ) permission = Permission.get(self.session, form.data["permission"]) if not permission: return self.notfound() allowed = False for perm in grantable: if perm[1] == permission.name: if matches_glob(perm[3], form.data["argument"]): allowed = True break if not allowed: form.argument.errors.append( "The group {} does not have that permission".format(group.name)) return self.render( "service-account-permission-grant.html", form=form, user=user, group=group, alerts=self.get_form_alerts(form.errors) ) try: grant_permission_to_service_account( self.session, service_account, permission, form.data["argument"]) except IntegrityError: self.session.rollback() return self.render( "service-account-permission-grant.html", form=form, user=user, alerts=self.get_form_alerts(form.errors) ) AuditLog.log(self.session, self.current_user.id, "grant_permission", "Granted permission with argument: {}".format(form.data["argument"]), on_permission_id=permission.id, on_group_id=group.id, on_user_id=service_account.user.id) return self.redirect("/groups/{}/service/{}?refresh=yes".format( group.name, service_account.user.username))
def revoke_permission_from_group(self, permission, argument, group): # type: (str, str, str) -> None permission_obj = Permission.get(self.session, name=permission) assert permission_obj group_obj = Group.get(self.session, name=group) assert group_obj self.session.query(PermissionMap).filter( PermissionMap.permission_id == permission_obj.id, PermissionMap.group_id == group_obj.id, PermissionMap.argument == argument, ).delete()
def get_permission(self, name): # type: (str) -> Optional[Permission] permission = SQLPermission.get(self.session, name=name) if not permission: return None return Permission( name=permission.name, description=permission.description, created_on=permission.created_on, audited=permission.audited, enabled=permission.enabled, )
def get_permission(session, name): # type: (Session, str) -> Optional[Permission] """Get a permission Arg(s): session(models.base.session.Session): database session name(str): the name of the permission Returns: The permission if found, None otherwise """ return Permission.get(session, name=name)
def test_permission_disable_denied(setup): # type: (SetupTest) -> None with setup.transaction(): setup.create_user("*****@*****.**") setup.create_permission("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_permission_denied("some-permission") ] assert Permission.get(setup.session, name="some-permission").enabled
def grant_permission_to_group(self, permission, argument, group): # type: (str, str, str) -> None self.create_group(group) self.create_permission(permission) permission_obj = Permission.get(self.session, name=permission) assert permission_obj group_obj = Group.get(self.session, name=group) assert group_obj grant = PermissionMap(permission_id=permission_obj.id, group_id=group_obj.id, argument=argument) grant.add(self.session)
def entries_affecting_permission(self, permission, limit): # type: (str, int) -> List[AuditLogEntry] permission_obj = Permission.get(self.session, name=permission) if not permission_obj: return [] results = ( self.session.query(AuditLog) .filter(AuditLog.on_permission_id == permission_obj.id) .order_by(desc(AuditLog.log_time)) .limit(limit) ) return [self._to_audit_log_entry(e) for e in results]
def grant_permission_to_group(self, permission, argument, group): # type: (str, str, str) -> None sql_group = Group.get(self.session, name=group) if not sql_group: raise GroupNotFoundException(group) sql_permission = Permission.get(self.session, name=permission) if not sql_permission: raise PermissionNotFoundException(permission) mapping = PermissionMap( permission_id=sql_permission.id, group_id=sql_group.id, argument=argument ) mapping.add(self.session)
def test_permission_disable_denied(setup): # type: (SetupTest) -> None with setup.transaction(): setup.create_user("*****@*****.**") setup.create_permission("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_permission_denied("some-permission") ] assert Permission.get(setup.session, name="some-permission").enabled
def post(self, name=None): tag = PublicKeyTag.get(self.session, None, name) if not tag: return self.notfound() if not user_has_permission(self.session, self.current_user, TAG_EDIT, tag.name): return self.forbidden() form = PermissionGrantTagForm(self.request.arguments) form.permission.choices = [["", "(select one)"]] for perm in self.session.query(Permission).all(): form.permission.choices.append( [perm.name, "{} (*)".format(perm.name)]) if not form.validate(): return self.render("permission-grant-tag.html", form=form, tag=tag, alerts=self.get_form_alerts(form.errors)) permission = Permission.get(self.session, form.data["permission"]) if not permission: return self.notfound() # Shouldn't happen. success = grant_permission_to_tag(self.session, tag.id, permission.id, argument=form.data["argument"]) if not success: form.argument.errors.append( "Permission and Argument already mapped to this tag.") return self.render( "permission-grant-tag.html", form=form, tag=tag, alerts=self.get_form_alerts(form.errors), ) AuditLog.log(self.session, self.current_user.id, 'grant_permission_tag', 'Granted permission with argument: {}'.format( form.data["argument"]), on_permission_id=permission.id, on_tag_id=tag.id) return self.redirect("/tags/{}?refresh=yes".format(tag.name))
def grant_permission_to_service_account(self, permission, argument, service_account): # type: (str, str, str) -> None self.create_permission(permission) permission_obj = Permission.get(self.session, name=permission) assert permission_obj user_obj = User.get(self.session, name=service_account) assert user_obj, "Must create the service account first" assert user_obj.is_service_account grant = ServiceAccountPermissionMap( permission_id=permission_obj.id, service_account_id=user_obj.service_account.id, argument=argument, ) grant.add(self.session)
def get(self, name=None): # TODO: use cached data instead, add refresh to appropriate redirects. permission = Permission.get(self.session, name) if not permission: return self.notfound() can_delete = self.current_user.permission_admin mapped_groups = get_groups_by_permission(self.session, permission) log_entries = get_log_entries_by_permission(self.session, permission) self.render( "permission.html", permission=permission, can_delete=can_delete, mapped_groups=mapped_groups, log_entries=log_entries, )
def grant_permission_to_service_account(self, permission, argument, service): # type: (str, str, str) -> None sql_service = ServiceAccount.get(self.session, name=service) if not sql_service or not sql_service.user.enabled: raise ServiceAccountNotFoundException(service) sql_permission = Permission.get(self.session, name=permission) if not sql_permission: raise PermissionNotFoundException(permission) mapping = ServiceAccountPermissionMap( permission_id=sql_permission.id, service_account_id=sql_service.id, argument=argument) mapping.add(self.session)
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 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_permission_disable(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group(PERMISSION_ADMIN, "", "admins") setup.add_user_to_group("*****@*****.**", "admins") setup.create_permission("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.disabled_permission("some-permission")] assert not Permission.get(setup.session, name="some-permission").enabled audit_log_service = setup.service_factory.create_audit_log_service() audit_log_entries = audit_log_service.entries_affecting_permission("some-permission", 20) assert len(audit_log_entries) == 1 assert audit_log_entries[0].actor == "*****@*****.**" assert audit_log_entries[0].action == "disable_permission" assert audit_log_entries[0].on_permission == "some-permission"
def permissions(session, users): # type: (Session, Dict[str, User]) -> Dict[str, Permission] """Create a standard set of test permissions. Go to a bit of effort to use unique timestamps for the creation date of permissions, since it makes it easier to test sorting. Similarly, don't sort the list of permissions to create by name so that the date sort and the name sort are different. Do not use milliseconds in the creation timestamps, since the result will be different in SQLite (where they are preserved) and MySQL (where they are stripped). """ all_permissions = [ "owner", "ssh", "sudo", "audited", AUDIT_MANAGER, AUDIT_VIEWER, PERMISSION_AUDITOR, PERMISSION_ADMIN, "team-sre", USER_ADMIN, GROUP_ADMIN, ] created_on_seconds = int(time() - 1000) permissions = {} for name in all_permissions: permission = Permission.get(session, name=name) if not permission: created_on = datetime.utcfromtimestamp(created_on_seconds) created_on_seconds += 1 description = "{} permission".format(name) permission = Permission(name=name, description=description, created_on=created_on) permission.add(session) permissions[name] = permission enable_permission_auditing(session, permissions["audited"].name, users["*****@*****.**"].id) return permissions
def get(self, name=None): # TODO: use cached data instead, add refresh to appropriate redirects. permission = Permission.get(self.session, name) if not permission: return self.notfound() can_change_audit_status = user_is_permission_admin( self.session, self.current_user) can_delete = user_is_permission_admin(self.session, self.current_user) mapped_groups = get_groups_by_permission(self.session, permission) log_entries = get_log_entries_by_permission(self.session, permission) self.render( "permission.html", permission=permission, can_delete=can_delete, mapped_groups=mapped_groups, log_entries=log_entries, can_change_audit_status=can_change_audit_status, )
def disable_permission_auditing(session, permission_name, actor_user_id): """Set a permission as audited. Args: session(models.base.session.Session): database session permission_name(str): name of permission in question actor_user_id(int): id of user who is disabling auditing """ permission = Permission.get(session, permission_name) if not permission: raise NoSuchPermission(name=permission_name) permission._audited = False AuditLog.log(session, actor_user_id, 'disable_auditing', 'Disabled auditing.', on_permission_id=permission.id) Counter.incr(session, "updates") session.commit()
def test_service_accounts(session, standard_graph, users, http_client, base_url): graph = standard_graph service_accounts = sorted([u.name for u in users.values() if u.role_user] + ["*****@*****.**"]) api_url = url(base_url, "/service_accounts") resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" assert sorted(body["data"]["service_accounts"]) == service_accounts # TODO: test cutoff # Retrieve a single service account and check its metadata. api_url = url(base_url, "/service_accounts/[email protected]") resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" data = body["data"]["user"] assert "service_account" in data assert data["service_account"]["description"] == "some service account" assert data["service_account"]["machine_set"] == "some machines" assert data["service_account"]["owner"] == "team-sre" assert body["data"]["permissions"] == [] # Delegate a permission to the service account and check for it. service_account = ServiceAccount.get(session, name="*****@*****.**") permission = Permission.get(session, name="team-sre") grant_permission_to_service_account(session, service_account, permission, "*") graph.update_from_db(session) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" permissions = body["data"]["permissions"] assert permissions[0]["permission"] == "team-sre" assert permissions[0]["argument"] == "*"
def post(self, name=None): tag = PublicKeyTag.get(self.session, None, name) if not tag: return self.notfound() if not user_has_permission(self.session, self.current_user, TAG_EDIT, tag.name): return self.forbidden() form = PermissionGrantTagForm(self.request.arguments) form.permission.choices = [["", "(select one)"]] for perm in self.session.query(Permission).all(): form.permission.choices.append([perm.name, "{} (*)".format(perm.name)]) if not form.validate(): return self.render( "permission-grant-tag.html", form=form, tag=tag, alerts=self.get_form_alerts(form.errors) ) permission = Permission.get(self.session, form.data["permission"]) if not permission: return self.notfound() # Shouldn't happen. success = grant_permission_to_tag(self.session, tag.id, permission.id, argument=form.data["argument"]) if not success: form.argument.errors.append( "Permission and Argument already mapped to this tag." ) return self.render( "permission-grant-tag.html", form=form, tag=tag, alerts=self.get_form_alerts(form.errors), ) AuditLog.log(self.session, self.current_user.id, 'grant_permission_tag', 'Granted permission with argument: {}'.format(form.data["argument"]), on_permission_id=permission.id, on_tag_id=tag.id) return self.redirect("/tags/{}?refresh=yes".format(tag.name))
def test_permission_disable(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group(PERMISSION_ADMIN, "", "admins") setup.add_user_to_group("*****@*****.**", "admins") setup.create_permission("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.disabled_permission("some-permission")] assert not Permission.get(setup.session, name="some-permission").enabled audit_log_service = setup.service_factory.create_audit_log_service() audit_log_entries = audit_log_service.entries_affecting_permission( "some-permission", 20) assert len(audit_log_entries) == 1 assert audit_log_entries[0].actor == "*****@*****.**" assert audit_log_entries[0].action == "disable_permission" assert audit_log_entries[0].on_permission == "some-permission"
def test_revoke_permission_from_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") fe_url = url(base_url, '/tags') resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'tagname': "tyler_was_here", "description": "Test Tag Please Ignore"}), headers={'X-Grouper-User': user.username}) tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/permissions/grant_tag/{}'.format(tag.name)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'permission': TAG_EDIT, "argument": "*"}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") perm = Permission.get(session, TAG_EDIT) assert len(get_public_key_tag_permissions(session, tag)) == 1, "The tag should have exactly 1 permission" user = session.query(User).filter_by(username="******").scalar() mapping = get_public_key_tag_permissions(session, tag)[0] fe_url = url(base_url, '/permissions/{}/revoke_tag/{}'.format(TAG_EDIT, mapping.mapping_id)) resp = yield http_client.fetch(fe_url, method="POST", body="", headers={'X-Grouper-User': user.username}) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") assert len(get_public_key_tag_permissions(session, tag)) == 0, "The tag should have no permissions"
def test_grant_permission_to_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") fe_url = url(base_url, '/tags') resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'tagname': "tyler_was_here", "description": "Test Tag Please Ignore"}), headers={'X-Grouper-User': user.username}) tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/permissions/grant_tag/{}'.format(tag.name)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'permission': TAG_EDIT, "argument": "*"}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") perm = Permission.get(session, TAG_EDIT) assert len(get_public_key_tag_permissions(session, tag)) == 1, "The tag should have exactly 1 permission" assert get_public_key_tag_permissions(session, tag)[0].name == perm.name, "The tag's permission should be the one we added" assert get_public_key_tag_permissions(session, tag)[0].argument == "*", "The tag's permission should be the one we added" # Make sure trying to add a permission to a tag doesn't fail horribly if it's already there user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/permissions/grant_tag/{}'.format(tag.name)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'permission': TAG_EDIT, "argument": "*"}), headers={'X-Grouper-User': user.username}) assert resp.code == 200
def create_permission(self, name, description="", audited=False, enabled=True, created_on=None): # type: (str, str, bool, bool, Optional[datetime]) -> None """Create a permission, does nothing if it already exists. Avoid milliseconds in the creation timestamp since they behave differently in SQLite (which preserves them) and MySQL (which drops them). """ if Permission.get(self.session, name=name): return if not created_on: created_on = datetime.utcfromtimestamp(int(time())) permission = Permission( name=name, description=description, _audited=audited, enabled=enabled, created_on=created_on, ) permission.add(self.session)
def disable_permission(self, name): # type: (str) -> None permission = SQLPermission.get(self.session, name=name) if not permission: raise PermissionNotFoundException(name) permission.enabled = False
def _id_for_permission(self, permission): # type: (str) -> int permission_obj = Permission.get(self.session, name=permission) if not permission_obj: raise PermissionNotFoundException(permission) return permission_obj.id
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() # only owner of group can request permissions for that group role_index = self.current_user.my_role_index(group.my_members()) if not role_index: return self.forbidden() # check inputs args_by_perm = get_grantable_permissions(self.session, settings.restricted_ownership_permissions) dropdown_form, text_form = GroupPermissionRequest._get_forms(args_by_perm, self.request.arguments) argument_type = self.request.arguments.get("argument_type") if argument_type and argument_type[0] == "text": form = text_form elif argument_type and argument_type[0] == "dropdown": form = dropdown_form form.argument.choices = [(a, a) for a in args_by_perm[form.permission_name.data]] else: # someone messing with the form self.log_message("unknown argument type", group_name=group.name, argument_type=argument_type) return self.forbidden() if not form.validate(): return self.render( "group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), alerts=self.get_form_alerts(form.errors), dropdown_help=settings.permission_request_dropdown_help, text_help=settings.permission_request_text_help, ) permission = Permission.get(self.session, form.permission_name.data) assert permission is not None, "our prefilled permission should exist or we have problems" # save off request try: permissions.create_request(self.session, self.current_user, group, permission, form.argument.data, form.reason.data) except permissions.RequestAlreadyGranted: alerts = [Alert("danger", "This group already has this permission and argument.")] except permissions.RequestAlreadyExists: alerts = [Alert("danger", "Request for permission and argument already exists, please wait patiently.")] except permissions.NoOwnersAvailable: self.log_message("prefilled perm+arg have no owner", group_name=group.name, permission_name=permission.name, argument=form.argument.data) alerts = [Alert("danger", "No owners available for requested permission and argument." " If this error persists please contact an adminstrator.")] else: alerts = None if alerts: return self.render( "group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), alerts=alerts, ) else: return self.redirect("/groups/{}".format(group.name))
def get_permission(session: Session, name: str) -> Optional[Permission]: return Permission.get(session, name=name)
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() # Only members can request permissions if not self.current_user.is_member(group.my_members()): return self.forbidden() # check inputs args_by_perm = get_grantable_permissions( self.session, settings.restricted_ownership_permissions) dropdown_form, text_form = GroupPermissionRequest._get_forms( args_by_perm, self.request.arguments) argument_type = self.request.arguments.get("argument_type") if argument_type and argument_type[0] == "text": form = text_form elif argument_type and argument_type[0] == "dropdown": form = dropdown_form form.argument.choices = [ (a, a) for a in args_by_perm[form.permission_name.data] ] else: # someone messing with the form self.log_message("unknown argument type", group_name=group.name, argument_type=argument_type) return self.forbidden() if not form.validate(): return self.render( "group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), alerts=self.get_form_alerts(form.errors), dropdown_help=settings.permission_request_dropdown_help, text_help=settings.permission_request_text_help, ) permission = Permission.get(self.session, form.permission_name.data) assert permission is not None, "our prefilled permission should exist or we have problems" # save off request try: request = permissions.create_request(self.session, self.current_user, group, permission, form.argument.data, form.reason.data) except permissions.RequestAlreadyGranted: alerts = [ Alert("danger", "This group already has this permission and argument.") ] except permissions.RequestAlreadyExists: alerts = [ Alert( "danger", "Request for permission and argument already exists, please wait patiently." ) ] except permissions.NoOwnersAvailable: self.log_message("prefilled perm+arg have no owner", group_name=group.name, permission_name=permission.name, argument=form.argument.data) alerts = [ Alert( "danger", "No owners available for requested permission and argument." " If this error persists please contact an adminstrator.") ] else: alerts = None if alerts: return self.render( "group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), alerts=alerts, ) else: return self.redirect("/permissions/requests/{}".format(request.id))
def post(self, name=None): grantable = self.current_user.my_grantable_permissions() if not grantable: return self.forbidden() group = Group.get(self.session, None, name) if not group: return self.notfound() form = PermissionGrantForm(self.request.arguments) form.permission.choices = [["", "(select one)"]] for perm in grantable: grantable_str = "{} ({})".format(perm[0].name, perm[1]) form.permission.choices.append([perm[0].name, grantable_str]) if not form.validate(): return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors) ) permission = Permission.get(self.session, form.data["permission"]) if not permission: return self.notfound() # Shouldn't happen. allowed = False for perm in grantable: if perm[0].name == permission.name: if matches_glob(perm[1], form.data["argument"]): allowed = True break if not allowed: form.argument.errors.append( "You do not have grant authority over that permission/argument combination." ) return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) # If the permission is audited, then see if the subtree meets auditing requirements. if permission.audited: fail_message = ("Permission is audited and this group (or a subgroup) contains " + "owners, np-owners, or managers who have not received audit training.") try: permission_ok = assert_controllers_are_auditors(group) except UserNotAuditor as e: permission_ok = False fail_message = e if not permission_ok: form.permission.errors.append(fail_message) return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) try: grant_permission(self.session, group.id, permission.id, argument=form.data["argument"]) except IntegrityError: form.argument.errors.append( "Permission and Argument already mapped to this group." ) return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) self.session.commit() AuditLog.log(self.session, self.current_user.id, 'grant_permission', 'Granted permission with argument: {}'.format(form.data["argument"]), on_permission_id=permission.id, on_group_id=group.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def test_grant_and_revoke(session, standard_graph, graph, groups, permissions, http_client, base_url): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): return any( map(lambda x: x.permission == permission_name, graph.permission_metadata[group_name])) # make some permission admins perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) # grant attempt by non-permission admin fe_url = url(base_url, "/permissions/grant/{}".format(group_name)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "specific_arg" }), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert not _check_graph_for_perm(graph), "no permissions granted" # grant by permission admin resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "specific_arg" }), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions granted, successfully" # figure out mapping_id of grant permission_id = Permission.get(session, name=permission_name).id group_id = Group.get(session, name=group_name).id mapping = session.query(PermissionMap).filter( PermissionMap.permission_id == permission_id, PermissionMap.group_id == group_id).first() # revoke permission by non-admin fe_url = url( base_url, "/permissions/{}/revoke/{}".format(permission_name, mapping.id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions not revoked" # revoke permission for realz resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert not _check_graph_for_perm(graph), "permissions revoked successfully"