def _sync_user_role_assignments(self, user_db, role_assignment_dbs, role_assignment_api): """ Synchronize role assignments for a particular user. :param user_db: User to synchronize the assignments for. :type user_db: :class:`UserDB` :param role_assignment_dbs: Existing user role assignments. :type role_assignment_dbs: ``list`` of :class:`UserRoleAssignmentDB` :param role_assignment_api: Role assignment API for a particular user. :param role_assignment_api: :class:`UserRoleAssignmentFileFormatAPI` :rtype: ``tuple`` """ db_role_names = [role_assignment_db.role for role_assignment_db in role_assignment_dbs] db_role_names = set(db_role_names) api_role_names = role_assignment_api.roles if role_assignment_api else [] api_role_names = set(api_role_names) # A list of new assignments which should be added to the database new_role_names = api_role_names.difference(db_role_names) # A list of assgignments which need to be updated in the database updated_role_names = db_role_names.intersection(api_role_names) # A list of assignments which should be removed from the database removed_role_names = db_role_names - api_role_names LOG.debug('New assignments for user "%s": %r' % (user_db.name, new_role_names)) LOG.debug('Updated assignments for user "%s": %r' % (user_db.name, updated_role_names)) LOG.debug('Removed assignments for user "%s": %r' % (user_db.name, removed_role_names)) # Build a list of role assignments to delete role_names_to_delete = updated_role_names.union(removed_role_names) role_assignment_dbs_to_delete = [ role_assignment_db for role_assignment_db in role_assignment_dbs if role_assignment_db.role in role_names_to_delete ] UserRoleAssignment.query(user=user_db.name, role__in=role_names_to_delete).delete() LOG.debug('Removed %s assignments for user "%s"' % (len(role_assignment_dbs_to_delete), user_db.name)) # Build a list of roles assignments to create role_names_to_create = new_role_names.union(updated_role_names) role_dbs_to_assign = Role.query(name__in=role_names_to_create) created_role_assignment_dbs = [] for role_db in role_dbs_to_assign: if role_db.name in role_assignment_api.roles: description = getattr(role_assignment_api, "description", None) else: description = None assignment_db = rbac_services.assign_role_to_user(role_db=role_db, user_db=user_db, description=description) created_role_assignment_dbs.append(assignment_db) LOG.debug('Created %s new assignments for user "%s"' % (len(role_dbs_to_assign), user_db.name)) return (created_role_assignment_dbs, role_assignment_dbs_to_delete)
def get_all_permission_grants_for_user(user_db, resource_uid=None, resource_types=None, permission_types=None): """ Retrieve all the permission grants for a particular user optionally filtering on: - Resource uid - Resource types - Permission types The result is a union of all the permission grants assigned to the roles which are assigned to the user. :rtype: ``list`` or :class:`PermissionGrantDB` """ role_names = UserRoleAssignment.query(user=user_db.name).only('role').scalar('role') permission_grant_ids = Role.query(name__in=role_names).scalar('permission_grants') permission_grant_ids = sum(permission_grant_ids, []) permission_grants_filters = {} permission_grants_filters['id__in'] = permission_grant_ids if resource_uid: permission_grants_filters['resource_uid'] = resource_uid if resource_types: permission_grants_filters['resource_type__in'] = resource_types if permission_types: permission_grants_filters['permission_types__in'] = permission_types permission_grant_dbs = PermissionGrant.query(**permission_grants_filters) return permission_grant_dbs
def get_all_permission_grants_for_user(user_db, resource_uid=None, resource_types=None, permission_types=None): """ Retrieve all the permission grants for a particular user optionally filtering on: - Resource uid - Resource types - Permission types The result is a union of all the permission grants assigned to the roles which are assigned to the user. :rtype: ``list`` or :class:`PermissionGrantDB` """ role_names = UserRoleAssignment.query(user=user_db.name).only("role").scalar("role") permission_grant_ids = Role.query(name__in=role_names).scalar("permission_grants") permission_grant_ids = sum(permission_grant_ids, []) permission_grants_filters = {} permission_grants_filters["id__in"] = permission_grant_ids if resource_uid: permission_grants_filters["resource_uid"] = resource_uid if resource_types: permission_grants_filters["resource_type__in"] = resource_types if permission_types: permission_grants_filters["permission_types__in"] = permission_types permission_grant_dbs = PermissionGrant.query(**permission_grants_filters) return permission_grant_dbs
def get_roles_for_user(user_db, include_remote=True): """ Retrieve all the roles assigned to the provided user. :param user_db: User to retrieve the roles for. :type user_db: :class:`UserDB` :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`RoleDB` """ if include_remote: queryset = UserRoleAssignment.query(user=user_db.name) else: # when upgrading from pre v2.3.0 when this field didn't exist yet # Note: We also include None for pre v2.3 when this field didn't exist yet queryset_filter = ( Q(user=user_db.name) & (Q(is_remote=False) | Q(is_remote__exists=False))) queryset = UserRoleAssignmentDB.objects(queryset_filter) role_names = queryset.only('role').scalar('role') result = Role.query(name__in=role_names) return result
def get_key_uids_for_user(user): role_names = UserRoleAssignment.query(user=user).only("role").scalar("role") permission_grant_ids = Role.query(name__in=role_names).scalar("permission_grants") permission_grant_ids = sum(permission_grant_ids, []) permission_grants_filters = {} permission_grants_filters["id__in"] = permission_grant_ids permission_grants_filters["resource_type"] = ResourceType.KEY_VALUE_PAIR return PermissionGrant.query(**permission_grants_filters).scalar("resource_uid")
def get_role_assignments_for_user(user_db, include_remote=True): """ Retrieve all the UserRoleAssignmentDB objects for a particular user. :param user_db: User to retrieve the role assignments for. :type user_db: :class:`UserDB` :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ if include_remote: result = UserRoleAssignment.query(user=user_db.name) else: result = UserRoleAssignment.query(user=user_db.name, is_remote=False) return result
def get_role_assignments_for_user(user_db): """ Retrieve all the UserRoleAssignmentDB objects for a particular user. :param user_db: User to retrieve the role assignments for. :type user_db: :class:`UserDB` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ result = UserRoleAssignment.query(user=user_db.name) return result
def get_roles_for_user(user_db, include_remote=True): """ Retrieve all the roles assigned to the provided user. :param user_db: User to retrieve the roles for. :type user_db: :class:`UserDB` :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`RoleDB` """ if include_remote: queryset = UserRoleAssignment.query(user=user_db.name) else: queryset = UserRoleAssignment.query(user=user_db.name, is_remote=False) role_names = queryset.only('role').scalar('role') result = Role.query(name__in=role_names) return result
def get_role_assignments_for_user(user_db): """ Retrieve all the UserRoleAssignmentDB objects for a particular user. :param user_db: User to retrieve the role assignments for. :type user_db: :class:`UserDB` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ result = UserRoleAssignment.query(user=user_db.name) return result
def get_roles_for_user(user_db): """ Retrieve all the roles assigned to the provided user. :param user_db: User to retrieve the roles for. :type user_db: :class:`UserDB` :rtype: ``list`` of :class:`RoleDB` """ role_names = UserRoleAssignment.query(user=user_db.name).only("role").scalar("role") result = Role.query(name__in=role_names) return result
def assign_role_to_user( role_db, user_db, description=None, is_remote=False, source=None, ignore_already_exists_error=False, ): """ Assign role to a user. :param role_db: Role to assign. :type role_db: :class:`RoleDB` :param user_db: User to assign the role to. :type user_db: :class:`UserDB` :param description: Optional assingment description. :type description: ``str`` :param include_remote: True if this a remote assignment. :type include_remote: ``bool`` :param source: Source from where this assignment comes from. For example, path of a file if it's a local assignment or mapping or "API". :type source: ``str`` :param: ignore_already_exists_error: True to ignore error if an assignment already exists. :type ignore_already_exists_error: ``bool`` """ role_assignment_db = UserRoleAssignmentDB( user=user_db.name, role=role_db.name, source=source, description=description, is_remote=is_remote, ) try: role_assignment_db = UserRoleAssignment.add_or_update( role_assignment_db) except (NotUniqueError, StackStormDBObjectConflictError) as e: if not ignore_already_exists_error: raise e role_assignment_db = UserRoleAssignment.query( user=user_db.name, role=role_db.name, source=source, description=description).first() return role_assignment_db
def get_roles_for_user(user_db): """ Retrieve all the roles assigned to the provided user. :param user_db: User to retrieve the roles for. :type user_db: :class:`UserDB` :rtype: ``list`` of :class:`RoleDB` """ role_names = UserRoleAssignment.query( user=user_db.name).only('role').scalar('role') result = Role.query(name__in=role_names) return result
def revoke_role_from_user(role_db, user_db): """ Revoke role from a user. :param role_db: Role to revoke. :type role_db: :class:`RoleDB` :param user_db: User to revoke the role from. :type user_db: :class:`UserDB` """ role_assignment_dbs = UserRoleAssignment.query(user=user_db.name, role=role_db.name) for role_assignment_db in role_assignment_dbs: UserRoleAssignment.delete(role_assignment_db)
def get_all_role_assignments(include_remote=True): """ Retrieve all the UserRoleAssignmentDB objects. :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ if include_remote: result = UserRoleAssignment.query() else: # Note: We also include documents with no "is_remote" field so it also works correctly # when upgrading from pre v2.3.0 when this field didn't exist yet queryset_filter = (Q(is_remote=False) | Q(is_remote__exists=False)) result = UserRoleAssignmentDB.objects(queryset_filter) return result
def get_all_role_assignments(include_remote=True): """ Retrieve all the UserRoleAssignmentDB objects. :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ if include_remote: result = UserRoleAssignment.query() else: # Note: We also include documents with no "is_remote" field so it also works correctly # when upgrading from pre v2.3.0 when this field didn't exist yet queryset_filter = (Q(is_remote=False) | Q(is_remote__exists=False)) result = UserRoleAssignmentDB.objects(queryset_filter) return result
def get_role_assignments_for_user(user_db, include_remote=True): """ Retrieve all the UserRoleAssignmentDB objects for a particular user. :param user_db: User to retrieve the role assignments for. :type user_db: :class:`UserDB` :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ if include_remote: result = UserRoleAssignment.query(user=user_db.name) else: # Note: We also include documents with no "is_remote" field so it also works correctly # when upgrading from pre v2.3.0 when this field didn't exist yet queryset_filter = (Q(user=user_db.name) & (Q(is_remote=False) | Q(is_remote__exists=False))) result = UserRoleAssignmentDB.objects(queryset_filter) return result
def get_role_assignments_for_user(user_db, include_remote=True): """ Retrieve all the UserRoleAssignmentDB objects for a particular user. :param user_db: User to retrieve the role assignments for. :type user_db: :class:`UserDB` :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ if include_remote: result = UserRoleAssignment.query(user=user_db.name) else: # Note: We also include documents with no "is_remote" field so it also works correctly # when upgrading from pre v2.3.0 when this field didn't exist yet queryset_filter = (Q(user=user_db.name) & (Q(is_remote=False) | Q(is_remote__exists=False))) result = UserRoleAssignmentDB.objects(queryset_filter) return result
def assign_role_to_user(role_db, user_db, description=None, is_remote=False, source=None, ignore_already_exists_error=False): """ Assign role to a user. :param role_db: Role to assign. :type role_db: :class:`RoleDB` :param user_db: User to assign the role to. :type user_db: :class:`UserDB` :param description: Optional assingment description. :type description: ``str`` :param include_remote: True if this a remote assignment. :type include_remote: ``bool`` :param source: Source from where this assignment comes from. For example, path of a file if it's a local assignment or mapping or "API". :type source: ``str`` :param: ignore_already_exists_error: True to ignore error if an assignment already exists. :type ignore_already_exists_error: ``bool`` """ role_assignment_db = UserRoleAssignmentDB(user=user_db.name, role=role_db.name, source=source, description=description, is_remote=is_remote) try: role_assignment_db = UserRoleAssignment.add_or_update(role_assignment_db) except (NotUniqueError, StackStormDBObjectConflictError) as e: if not ignore_already_exists_error: raise e role_assignment_db = UserRoleAssignment.query(user=user_db.name, role=role_db.name, source=source, description=description).first() return role_assignment_db
def get_roles_for_user(user_db, include_remote=True): """ Retrieve all the roles assigned to the provided user. :param user_db: User to retrieve the roles for. :type user_db: :class:`UserDB` :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`RoleDB` """ if include_remote: queryset = UserRoleAssignment.query(user=user_db.name) else: # when upgrading from pre v2.3.0 when this field didn't exist yet # Note: We also include None for pre v2.3 when this field didn't exist yet queryset_filter = (Q(user=user_db.name) & (Q(is_remote=False) | Q(is_remote__exists=False))) queryset = UserRoleAssignmentDB.objects(queryset_filter) role_names = queryset.only('role').scalar('role') result = Role.query(name__in=role_names) return result
def sync(self, user_db, groups): """ :param user_db: User to sync the assignments for. :type user: :class:`UserDB` :param groups: A list of remote groups user is a member of. :type groups: ``list`` of ``str`` :return: A list of mappings which have been created. :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ groups = list(set(groups)) extra = {'user_db': user_db, 'groups': groups} LOG.info('Synchronizing remote role assignments for user "%s"' % (str(user_db)), extra=extra) # 1. Retrieve group to role mappings for the provided groups all_mapping_dbs = GroupToRoleMapping.query(group__in=groups) enabled_mapping_dbs = [mapping_db for mapping_db in all_mapping_dbs if mapping_db.enabled] disabled_mapping_dbs = [mapping_db for mapping_db in all_mapping_dbs if not mapping_db.enabled] if not all_mapping_dbs: LOG.debug('No group to role mappings found for user "%s"' % (str(user_db)), extra=extra) # 2. Remove all the existing remote role assignments remote_assignment_dbs = UserRoleAssignment.query(user=user_db.name, is_remote=True) existing_role_names = [assignment_db.role for assignment_db in remote_assignment_dbs] existing_role_names = set(existing_role_names) current_role_names = set([]) for mapping_db in all_mapping_dbs: for role in mapping_db.roles: current_role_names.add(role) # A list of new role assignments which should be added to the database new_role_names = current_role_names.difference(existing_role_names) # A list of role assignments which need to be updated in the database updated_role_names = existing_role_names.intersection(current_role_names) # A list of role assignments which should be removed from the database removed_role_names = (existing_role_names - new_role_names) # Also remove any assignments for mappings which are disabled in the database for mapping_db in disabled_mapping_dbs: for role in mapping_db.roles: removed_role_names.add(role) LOG.debug('New role assignments: %r' % (new_role_names)) LOG.debug('Updated role assignments: %r' % (updated_role_names)) LOG.debug('Removed role assignments: %r' % (removed_role_names)) # Build a list of role assignments to delete role_names_to_delete = updated_role_names.union(removed_role_names) role_assignment_dbs_to_delete = [role_assignment_db for role_assignment_db in remote_assignment_dbs if role_assignment_db.role in role_names_to_delete] UserRoleAssignment.query(user=user_db.name, role__in=role_names_to_delete, is_remote=True).delete() # 3. Create role assignments for all the current groups created_assignments_dbs = [] for mapping_db in enabled_mapping_dbs: extra['mapping_db'] = mapping_db for role_name in mapping_db.roles: role_db = rbac_services.get_role_by_name(name=role_name) if not role_db: # Gracefully skip assignment for role which doesn't exist in the db LOG.info('Role with name "%s" for mapping "%s" not found, skipping assignment.' % (role_name, str(mapping_db)), extra=extra) continue description = ('Automatic role assignment based on the remote user membership in ' 'group "%s"' % (mapping_db.group)) assignment_db = rbac_services.assign_role_to_user(role_db=role_db, user_db=user_db, description=description, is_remote=True, source=mapping_db.source, ignore_already_exists_error=True) assert assignment_db.is_remote is True created_assignments_dbs.append(assignment_db) LOG.debug('Created %s new remote role assignments for user "%s"' % (len(created_assignments_dbs), str(user_db)), extra=extra) return (created_assignments_dbs, role_assignment_dbs_to_delete)
def sync(self, user_db, groups): """ :param user_db: User to sync the assignments for. :type user: :class:`UserDB` :param groups: A list of remote groups user is a member of. :type groups: ``list`` of ``str`` :return: A list of mappings which have been created. :rtype: ``list`` of :class:`UserRoleAssignmentDB` """ groups = list(set(groups)) extra = {'user_db': user_db, 'groups': groups} LOG.info('Synchronizing remote role assignments for user "%s"' % (str(user_db)), extra=extra) # 1. Retrieve group to role mappings for the provided groups all_mapping_dbs = GroupToRoleMapping.query(group__in=groups) enabled_mapping_dbs = [ mapping_db for mapping_db in all_mapping_dbs if mapping_db.enabled ] disabled_mapping_dbs = [ mapping_db for mapping_db in all_mapping_dbs if not mapping_db.enabled ] if not all_mapping_dbs: LOG.debug('No group to role mappings found for user "%s"' % (str(user_db)), extra=extra) # 2. Remove all the existing remote role assignments remote_assignment_dbs = UserRoleAssignment.query(user=user_db.name, is_remote=True) existing_role_names = [ assignment_db.role for assignment_db in remote_assignment_dbs ] existing_role_names = set(existing_role_names) current_role_names = set([]) for mapping_db in all_mapping_dbs: for role in mapping_db.roles: current_role_names.add(role) # A list of new role assignments which should be added to the database new_role_names = current_role_names.difference(existing_role_names) # A list of role assignments which need to be updated in the database updated_role_names = existing_role_names.intersection( current_role_names) # A list of role assignments which should be removed from the database removed_role_names = (existing_role_names - new_role_names) # Also remove any assignments for mappings which are disabled in the database for mapping_db in disabled_mapping_dbs: for role in mapping_db.roles: removed_role_names.add(role) LOG.debug('New role assignments: %r' % (new_role_names)) LOG.debug('Updated role assignments: %r' % (updated_role_names)) LOG.debug('Removed role assignments: %r' % (removed_role_names)) # Build a list of role assignments to delete role_names_to_delete = updated_role_names.union(removed_role_names) role_assignment_dbs_to_delete = [ role_assignment_db for role_assignment_db in remote_assignment_dbs if role_assignment_db.role in role_names_to_delete ] UserRoleAssignment.query(user=user_db.name, role__in=role_names_to_delete, is_remote=True).delete() # 3. Create role assignments for all the current groups created_assignments_dbs = [] for mapping_db in enabled_mapping_dbs: extra['mapping_db'] = mapping_db for role_name in mapping_db.roles: role_db = rbac_services.get_role_by_name(name=role_name) if not role_db: # Gracefully skip assignment for role which doesn't exist in the db LOG.info( 'Role with name "%s" for mapping "%s" not found, skipping assignment.' % (role_name, str(mapping_db)), extra=extra) continue description = ( 'Automatic role assignment based on the remote user membership in ' 'group "%s"' % (mapping_db.group)) assignment_db = rbac_services.assign_role_to_user( role_db=role_db, user_db=user_db, description=description, is_remote=True, source=mapping_db.source, ignore_already_exists_error=True) assert assignment_db.is_remote is True created_assignments_dbs.append(assignment_db) LOG.debug('Created %s new remote role assignments for user "%s"' % (len(created_assignments_dbs), str(user_db)), extra=extra) return (created_assignments_dbs, role_assignment_dbs_to_delete)
def _sync_user_role_assignments(self, user_db, role_assignment_dbs, role_assignment_api): """ Synchronize role assignments for a particular user. :param user_db: User to synchronize the assignments for. :type user_db: :class:`UserDB` :param role_assignment_dbs: Existing user role assignments. :type role_assignment_dbs: ``list`` of :class:`UserRoleAssignmentDB` :param role_assignment_api: Role assignment API for a particular user. :param role_assignment_api: :class:`UserRoleAssignmentFileFormatAPI` :rtype: ``tuple`` """ db_role_names = [role_assignment_db.role for role_assignment_db in role_assignment_dbs] db_role_names = set(db_role_names) api_role_names = role_assignment_api.roles if role_assignment_api else [] api_role_names = set(api_role_names) # A list of new assignments which should be added to the database new_role_names = api_role_names.difference(db_role_names) # A list of assignments which need to be updated in the database updated_role_names = db_role_names.intersection(api_role_names) # A list of assignments which should be removed from the database removed_role_names = (db_role_names - api_role_names) LOG.debug('New assignments for user "%s": %r' % (user_db.name, new_role_names)) LOG.debug('Updated assignments for user "%s": %r' % (user_db.name, updated_role_names)) LOG.debug('Removed assignments for user "%s": %r' % (user_db.name, removed_role_names)) # Build a list of role assignments to delete role_names_to_delete = updated_role_names.union(removed_role_names) role_assignment_dbs_to_delete = [role_assignment_db for role_assignment_db in role_assignment_dbs if role_assignment_db.role in role_names_to_delete] UserRoleAssignment.query(user=user_db.name, role__in=role_names_to_delete, is_remote=False).delete() LOG.debug('Removed %s assignments for user "%s"' % (len(role_assignment_dbs_to_delete), user_db.name)) # Build a list of roles assignments to create role_names_to_create = new_role_names.union(updated_role_names) role_dbs_to_assign = Role.query(name__in=role_names_to_create) created_role_assignment_dbs = [] for role_db in role_dbs_to_assign: if role_db.name in role_assignment_api.roles: description = getattr(role_assignment_api, 'description', None) else: description = None assignment_db = rbac_services.assign_role_to_user(role_db=role_db, user_db=user_db, description=description) created_role_assignment_dbs.append(assignment_db) LOG.debug('Created %s new assignments for user "%s"' % (len(role_dbs_to_assign), user_db.name)) return (created_role_assignment_dbs, role_assignment_dbs_to_delete)