UserGroupService.java

package de.dlr.shepard.auth.users.services;

import de.dlr.shepard.auth.permission.io.PermissionsIO;
import de.dlr.shepard.auth.permission.model.Permissions;
import de.dlr.shepard.auth.permission.model.Roles;
import de.dlr.shepard.auth.permission.services.PermissionsService;
import de.dlr.shepard.auth.security.AuthenticationContext;
import de.dlr.shepard.auth.users.daos.UserGroupDAO;
import de.dlr.shepard.auth.users.entities.User;
import de.dlr.shepard.auth.users.entities.UserGroup;
import de.dlr.shepard.auth.users.io.UserGroupIO;
import de.dlr.shepard.common.exceptions.InvalidAuthException;
import de.dlr.shepard.common.exceptions.InvalidPathException;
import de.dlr.shepard.common.util.AccessType;
import de.dlr.shepard.common.util.DateHelper;
import de.dlr.shepard.common.util.PermissionType;
import de.dlr.shepard.common.util.QueryParamHelper;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@RequestScoped
public class UserGroupService {

  @Inject
  AuthenticationContext authenticationContext;

  @Inject
  UserGroupDAO userGroupDAO;

  @Inject
  UserService userService;

  @Inject
  PermissionsService permissionsService;

  @Inject
  DateHelper dateHelper;

  /**
   * Gets userGroup by userGroupId
   *
   * @param userGroupId
   * @return UserGroup
   * @throws InvalidPathException if user group could not be found by id
   * @throws InvalidAuthException if user has no read permissions on usergroup
   */
  public UserGroup getUserGroup(Long userGroupId) {
    UserGroup group = getUserGroupOptional(userGroupId).orElseThrow(() ->
      new InvalidPathException(String.format("ID ERROR - User Group with id %s is null or deleted", userGroupId))
    );
    assertIsAllowedToReadUserGroup(userGroupId);
    return group;
  }

  /**
   * Gets userGroup by userGroupId.
   *
   * No additional checks like a read permission check are performed.
   * @param userGroupId
   * @return Optional<UserGroup>
   */
  public Optional<UserGroup> getUserGroupOptional(Long userGroupId) {
    UserGroup group = userGroupDAO.findByNeo4jId(userGroupId);
    if (group == null || group.isDeleted()) {
      return Optional.empty();
    }
    return Optional.of(group);
  }

  public List<UserGroup> getAllUserGroups(QueryParamHelper params) {
    return userGroupDAO.findAllUserGroups(params, authenticationContext.getCurrentUserName());
  }

  public UserGroup createUserGroup(UserGroupIO userGroup) {
    var user = userService.getCurrentUser();
    var toCreate = new UserGroup();
    toCreate.setName(userGroup.getName());
    toCreate.setCreatedBy(user);
    toCreate.setCreatedAt(dateHelper.getDate());
    toCreate.setUsers(fetchUsers(userGroup.getUsernames()));
    var created = userGroupDAO.createOrUpdate(toCreate);
    permissionsService.createPermissions(created, user, PermissionType.Private);
    return created;
  }

  /**
   * Updates usergroup by id
   *
   * @param id
   * @param userGroup
   * @return UserGroup
   * @throws InvalidPathException if user group could not be found by id
   * @throws InvalidAuthException if user has no read or edit permissions on usergroup
   */
  public UserGroup updateUserGroup(Long id, UserGroupIO userGroup) {
    getUserGroup(id);
    assertIsAllowedToEditUserGroup(id);

    var user = userService.getCurrentUser();
    var old = userGroupDAO.findByNeo4jId(id);
    old.setUpdatedBy(user);
    old.setUpdatedAt(dateHelper.getDate());
    old.setName(userGroup.getName());
    old.setUsers(fetchUsers(userGroup.getUsernames()));
    var updated = userGroupDAO.createOrUpdate(old);
    return updated;
  }

  /**
   * Deletes a user group and removes the permissions
   * @param id
   * @throws InvalidPathException if user group could not be found by id
   * @throws InvalidAuthException if user has no read or edit permissions on usergroup
   * @throws NotFoundException if the usergroup's permissions could not be retrieved or deleted, or if permissions could not be found on entity with id
   */
  public void deleteUserGroup(Long id) {
    getUserGroup(id);
    assertIsAllowedToEditUserGroup(id);

    Optional<Permissions> permissions = permissionsService.getPermissionsOfEntityOptional(id);
    if (permissions.isPresent() && !permissionsService.deletePermissions(permissions.get())) {
      String errorMsg = String.format("Could not delete permissions %s", permissions.toString());
      Log.error(errorMsg);
      throw new NotFoundException(errorMsg);
    }
    if (!userGroupDAO.deleteByNeo4jId(id)) {
      String errorMsg = String.format("Could not delete userGroup with id %s", id);
      Log.error(errorMsg);
      throw new NotFoundException(errorMsg);
    }
  }

  public Roles getUserGroupRoles(long groupId) {
    getUserGroup(groupId);

    return permissionsService.getUserRolesOnEntity(groupId, authenticationContext.getCurrentUserName());
  }

  public Permissions getUserGroupPermissions(long groupId) {
    getUserGroup(groupId);
    assertIsAllowedToManageUserGroup(groupId);

    return permissionsService.getPermissionsOfEntity(groupId);
  }

  public Permissions updateUserGroupPermissions(PermissionsIO newPermissions, long groupId) {
    getUserGroup(groupId);
    assertIsAllowedToManageUserGroup(groupId);

    return permissionsService.updatePermissionsByNeo4jId(newPermissions, groupId);
  }

  /**
   * Checks if the user requested the UserGroup is allowed to read it
   *
   * @throws InvalidAuthException when user is not allowed to read the UserGroup
   */
  public void assertIsAllowedToReadUserGroup(long groupId) {
    if (
      !permissionsService.isAccessTypeAllowedForUser(
        groupId,
        AccessType.Read,
        authenticationContext.getCurrentUserName()
      )
    ) {
      throw new InvalidAuthException("The requested action is forbidden by the permission policies");
    }
  }

  /**
   * Checks if the user requested the UserGroup is allowed to edit it
   *
   * @throws InvalidAuthException when user is not allowed to edit the UserGroup
   */
  public void assertIsAllowedToEditUserGroup(long groupId) {
    if (
      !permissionsService.isAccessTypeAllowedForUser(
        groupId,
        AccessType.Write,
        authenticationContext.getCurrentUserName()
      )
    ) {
      throw new InvalidAuthException("The requested action is forbidden by the permission policies");
    }
  }

  /**
   * Checks if the user requested the UserGroup is allowed to manage it
   *
   * @throws InvalidAuthException when user is not allowed to manage the UserGroup
   */
  public void assertIsAllowedToManageUserGroup(long groupId) {
    if (
      !permissionsService.isAccessTypeAllowedForUser(
        groupId,
        AccessType.Manage,
        authenticationContext.getCurrentUserName()
      )
    ) {
      throw new InvalidAuthException("The requested action is forbidden by the permission policies");
    }
  }

  private ArrayList<User> fetchUsers(String[] usernames) {
    var result = new ArrayList<User>(usernames.length);
    for (var username : usernames) {
      if (username == null) {
        continue;
      }
      userService.getUserOptional(username).ifPresent(u -> result.add(u));
    }
    return result;
  }
}