def disable_user(session, user): """Disables an enabled user""" get_plugin_proxy().will_disable_user(session, user) user.enabled = False Counter.incr(session, "updates")
def just_created(self): # type: () -> None """Call the user_created plugin on new User creation.""" # This is a little weird because the default value of the column isn't applied in the # object at the time this is called, so role_user may be None instead of False. is_service_account = self.role_user is not None and self.role_user get_plugin_proxy().user_created(self, is_service_account)
def set_defaults(): # type: () -> None instance = task_id() if instance is None: instance = 0 default_tags = {"instance": str(instance)} get_plugin_proxy().set_default_stats_tags(default_tags)
def persist_group_member_changes(session, group, requester, member, status, reason, create_edge=False, **updates): requested_at = datetime.utcnow() if "role" in updates: role = updates["role"] _validate_role(member.member_type, role) get_plugin_proxy().will_update_group_membership(session, group, member, **updates) if create_edge: edge = _create_edge(session, group, member, updates.get("role", "member")) else: edge = _get_edge(session, group, member) if not edge: raise MemberNotFound() changes = _serialize_changes(edge, **updates) request = Request( requester_id=requester.id, requesting_id=group.id, on_behalf_obj_type=member.member_type, on_behalf_obj_pk=member.id, requested_at=requested_at, edge_id=edge.id, status=status, changes=changes, ).add(session) session.flush() request_status_change = RequestStatusChange( request=request, user_id=requester.id, to_status=status, change_at=requested_at, ).add(session) session.flush() Comment( obj_type=OBJ_TYPES["RequestStatusChange"], obj_pk=request_status_change.id, user_id=requester.id, comment=reason, created_on=requested_at, ).add(session) session.flush() if status == "actioned": edge.apply_changes(request.changes) session.flush() Counter.incr(session, "updates") return request
def _check_machine_set(service_account, machine_set): # type: (ServiceAccount, str) -> None """Verify a service account machine set with plugins. Raises: BadMachineSet: if some plugin rejected the machine set """ try: get_plugin_proxy().check_machine_set(service_account.user.username, machine_set) except PluginRejectedMachineSet as e: raise BadMachineSet(str(e))
def add_public_key(session, user, public_key_str): """Add a public key for a particular user. Args: session: db session user: User model of user in question public_key_str: public key to add Throws: DuplicateKey if key is already in use PublicKeyParseError if key can't be parsed BadPublicKey if a plugin rejects the key Returns: PublicKey model object representing the key """ pubkey = sshpubkeys.SSHKey(public_key_str, strict=True) try: pubkey.parse() except sshpubkeys.InvalidKeyException as e: raise PublicKeyParseError(str(e)) try: get_plugin_proxy().will_add_public_key(pubkey) except PluginRejectedPublicKey as e: raise BadPublicKey(str(e)) db_pubkey = PublicKey( user=user, public_key=pubkey.keydata.strip(), fingerprint=pubkey.hash_md5().replace("MD5:", ""), fingerprint_sha256=pubkey.hash_sha256().replace("SHA256:", ""), key_size=pubkey.bits, key_type=pubkey.key_type, comment=pubkey.comment, ) try: db_pubkey.add(session) Counter.incr(session, "updates") except IntegrityError: session.rollback() raise DuplicateKey() session.commit() return db_pubkey
def run(self, session, dry_run=True): for key in session.query(PublicKey): pubkey = sshpubkeys.SSHKey(key.public_key, strict=True) logging.info("Processing Key (id={})".format(key.id)) try: pubkey.parse() except sshpubkeys.InvalidKeyException as e: logging.error("Invalid Key (id={}): {}".format(key.id, e.message)) continue try: get_plugin_proxy().will_add_public_key(pubkey) except PluginRejectedPublicKey as e: logging.error("Bad Key (id={}): {}".format(key.id, e.message)) continue
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 log(session, actor_id, action, description, on_user_id=None, on_group_id=None, on_permission_id=None, on_tag_id=None, category=AuditLogCategory.general): """ Log an event in the database. Args: session(Session): database session actor_id(int): actor action(str): unique string identifier for action taken description(str): description for action taken on_user_id(int): user affected, if any on_group_id(int): group affected, if any on_permission_id(int): permission affected, if any category(AuditLogCategory): category of log entry """ entry = AuditLog( actor_id=actor_id, log_time=datetime.utcnow(), action=action, description=description, on_user_id=on_user_id if on_user_id else None, on_group_id=on_group_id if on_group_id else None, on_permission_id=on_permission_id if on_permission_id else None, on_tag_id=on_tag_id if on_tag_id else None, category=int(category), ) try: entry.add(session) session.flush() except IntegrityError: session.rollback() raise AuditLogFailure() session.commit() get_plugin_proxy().log_auditlog_entry(entry)
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 initialize(self, *args, **kwargs): # type: (*Any, **Any) -> None self.graph = Graph() self.session = self.settings["session"]() # type: Session self.template_engine = self.settings["template_engine"] # type: FrontendTemplateEngine self.plugins = get_plugin_proxy() session_factory = SingletonSessionFactory(self.session) self.usecase_factory = create_graph_usecase_factory( settings(), self.plugins, session_factory ) if self.get_argument("_profile", False): self.perf_collector = Collector() self.perf_trace_uuid = str(uuid4()) # type: Optional[str] self.perf_collector.start() else: self.perf_collector = None self.perf_trace_uuid = None self._request_start_time = datetime.utcnow() stats.log_rate("requests", 1) stats.log_rate("requests_{}".format(self.__class__.__name__), 1)
def _get_permission_metadata(session): ''' Returns a dict of groupname: { list of permissions }. ''' out = defaultdict(list) # groupid -> [ ... ] permissions = session.query(Permission, PermissionMap).filter( Permission.id == PermissionMap.permission_id, PermissionMap.group_id == Group.id, Group.enabled == True, ) for (permission, permission_map) in permissions: out[permission_map.group.name].append(MappedPermission( permission=permission.name, audited=permission.audited, argument=permission_map.argument, groupname=permission_map.group.name, granted_on=permission_map.granted_on, alias=False, )) aliases = get_plugin_proxy().get_aliases_for_mapped_permission( session, permission.name, permission_map.argument ) for (name, arg) in aliases: out[permission_map.group.name].append(MappedPermission( permission=name, audited=permission.audited, argument=arg, groupname=permission_map.group.name, granted_on=permission_map.granted_on, alias=True, )) return out
def log_rate(key, val, count=1): # type: (str, float, int) -> None get_plugin_proxy().log_rate(key, val, count)
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 get_owners_by_grantable_permission( session: Session, separate_global: bool = False) -> Dict[object, Dict[str, List[Group]]]: """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: Database session separate_global: Whether to construct a specific entry for GLOBAL_OWNER in the output map Returns: A map of permission to argument to owners of the form: {permission: {argument: [owner1, ...], }, } where owners are Group objects. argument can be '*' which means anything. """ all_permissions = { permission.name: permission for permission in get_all_permissions(session) } all_groups = session.query(Group).filter(Group.enabled == True).all() owners_by_arg_by_perm: Dict[object, Dict[str, List[Group]]] = 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: Dict[str, List[Any]] = 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([g.name == PERMISSION_ADMIN for g in 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 permission_name, owners_by_arg in res.items(): for arg, owners in owners_by_arg.items(): owners_by_arg_by_perm[permission_name][arg] += owners return owners_by_arg_by_perm
def test_permission_grant_to_owners( session, standard_graph, groups, grantable_permissions, permissions # noqa: F811 ): """Test we're getting correct owners according to granted 'grouper.permission.grant' permissions.""" perm_grant, _, perm1, perm2 = grantable_permissions # Disable the group with permission admin since otherwise they're an approver on everything, # and check that there are then no approvers. groups["permission-admins"].disable() session.commit() 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.groupname for o, a in get_owner_arg_list( session, perm1, "somesubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == ["all-teams", "team-sre" ], "should include substring wildcard matches" res = [ o.groupname for o, a in get_owner_arg_list( session, perm1, "othersubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == ["all-teams" ], "negative test of substring wildcard matches" class FakePermissionAliasesPlugin(BasePlugin): def get_aliases_for_mapped_permission(self, session, permission, argument): # type: (Session, str, str) -> List[Tuple[str, str]] if permission != "alias_perm" or argument != "team-sre": return [] return [(PERMISSION_GRANT, "foo-perm/bar-arg")] owner_perm = create_permission(session, "alias_perm") session.commit() get_plugin_proxy().add_plugin(FakePermissionAliasesPlugin()) grant_permission(groups["team-sre"], owner_perm, "team-sre") owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups["team-sre"]] assert owners_by_arg_by_perm["foo-perm"]["bar-arg"] == expected # permission admins have all the power grant_permission(groups["security-team"], permissions[PERMISSION_ADMIN]) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) all_permissions = get_all_permissions(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 persist_group_member_changes( session, # type: Session group, # type: Group requester, # type: User member, # type: Union[User, Group] status, # type: str reason, # type: str create_edge=False, # type: bool **updates # type: Any ): # type: (...) -> Request requested_at = datetime.utcnow() if "role" in updates: role = updates["role"] _validate_role(member.member_type, role) get_plugin_proxy().will_update_group_membership(session, group, member, **updates) if create_edge: edge = _create_edge(session, group, member, updates.get("role", "member")) else: edge = _get_edge(session, group, member) if not edge: raise MemberNotFound() changes = _serialize_changes(edge, **updates) request = Request( requester_id=requester.id, requesting_id=group.id, on_behalf_obj_type=member.member_type, on_behalf_obj_pk=member.id, requested_at=requested_at, edge_id=edge.id, status=status, changes=changes, ).add(session) session.flush() request_status_change = RequestStatusChange( request=request, user_id=requester.id, to_status=status, change_at=requested_at).add(session) session.flush() Comment( obj_type=OBJ_TYPES["RequestStatusChange"], obj_pk=request_status_change.id, user_id=requester.id, comment=reason, created_on=requested_at, ).add(session) session.flush() if status == "actioned": edge.apply_changes(request.changes) session.flush() Counter.incr(session, "updates") return request
def test_permission_plugin(session, grantable_permissions, http_client, base_url): # noqa: F811 get_plugin_proxy().add_plugin(PermissionValidationPlugin()) groupname = "serving-team" username = "******" permission_name = "grantable.one" # Permission request checks # Rejected fe_url = url(base_url, f"/permissions/request?group={groupname}") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "quite-evil", "reason": "blah blah black sheep", }), headers={"X-Grouper-User": username}, ) assert resp.code == 200 assert b"RFC 3514" in resp.body # Accepted resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "not-bad", "reason": "blah blah black sheep", }), headers={"X-Grouper-User": username}, ) assert resp.code == 200 assert b"RFC 3514" not in resp.body # Permission grant checks user_name = "*****@*****.**" # Rejected fe_url = url(base_url, f"/permissions/grant/{groupname}") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "dastardly-evil" }), headers={"X-Grouper-User": user_name}, ) assert resp.code == 200 assert b"RFC 3514" in resp.body # Accepted fe_url = url(base_url, f"/permissions/grant/{groupname}") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "not-bad" }), headers={"X-Grouper-User": user_name}, ) assert resp.code == 200 assert b"RFC 3514" not in resp.body
def log_gauge(key, val): # type: (str, float) -> None get_plugin_proxy().log_gauge(key, val)