FolderManagers.java

/*
 * Copyright (C) 2000 - 2026 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.kmelia.control;

import org.silverpeas.components.kmelia.InstanceParameters;
import org.silverpeas.core.admin.component.model.ComponentInst;
import org.silverpeas.core.admin.user.model.ProfileInst;
import org.silverpeas.core.admin.user.model.SilverpeasRole;
import org.silverpeas.core.security.authorization.ForbiddenRuntimeException;
import org.silverpeas.core.web.selection.Selection;
import org.silverpeas.kernel.util.StringUtil;

import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

/**
 * The managers of the folders having their own access rights in a Kmelia instance. When it is
 * enabled in the Kmelia instance, the managers of the application are forced as managers of such
 * folders so that they keep the hand on them: they are set as managers of the folders and, once
 * they are, they cannot be removed from this role.
 * @author mmoquillon
 */
public class FolderManagers {

  private final boolean appManagersForced;
  private final Supplier<ProfileInst> appManagers;

  /**
   * Constructs the managers of the folders of a Kmelia instance.
   * @param appManagersForced are the application managers forced as managers of the folders
   * having their own access rights?
   * @param appManagers a supplier of the profile of the application managers. It is invoked only
   * when the application managers are forced as managers of the folders.
   */
  public FolderManagers(boolean appManagersForced, Supplier<ProfileInst> appManagers) {
    this.appManagersForced = appManagersForced;
    this.appManagers = appManagers;
  }

  /**
   * Are the application managers forced as managers of the folders having their own access rights
   * in the specified Kmelia instance?
   * @param instance a Kmelia instance.
   * @return true if the forcing of the application managers as folder managers is enabled in the
   * instance, false otherwise.
   */
  public static boolean areAppManagersForcedIn(ComponentInst instance) {
    return StringUtil.getBooleanValue(
        instance.getParameterValue(InstanceParameters.APP_MANAGERS_FORCED_AS_FOLDER_MANAGERS));
  }

  /**
   * Are the specific access rights on the folders enabled in the specified Kmelia instance? When
   * disabled, the access rights of the folders are kept as they are to be found again once
   * enabled.
   * @param instance a Kmelia instance.
   * @return true if the folders can have their own access rights in the instance, false otherwise.
   */
  public static boolean areRightsOnFoldersEnabledIn(ComponentInst instance) {
    return StringUtil.getBooleanValue(
        instance.getParameterValue(InstanceParameters.RIGHTS_ON_TOPICS));
  }

  /**
   * Gets the managers of the specified application: those explicitly set and those inherited
   * from the parent space.
   * @param instance a component instance.
   * @return the profile of the managers of the application.
   */
  public static ProfileInst applicationManagersOf(ComponentInst instance) {
    ProfileInst managers = new ProfileInst();
    managers.setName(SilverpeasRole.ADMIN.getName());
    instance.getAllProfilesInst().stream()
        .filter(FolderManagers::isManagers)
        .forEach(p -> {
          managers.addUsers(p.getAllUsers());
          managers.addGroups(p.getAllGroups());
        });
    return managers;
  }

  /**
   * Checks the update of the specified profile of a folder with the given users and groups
   * doesn't remove any manager of the application from the managers of the folder when the
   * application managers are forced as managers of the folders. Any other profile than the one of
   * the folder managers isn't concerned by this check.
   * @param folderProfile the actual profile of a folder having its own access rights.
   * @param userIds the identifiers of the users to set in the profile.
   * @param groupIds the identifiers of the user groups to set in the profile.
   * @throws ForbiddenRuntimeException if a manager of the application is removed from the
   * managers of the folder whereas the application managers are forced as folder managers.
   */
  public void checkNoApplicationManagerRemoved(ProfileInst folderProfile, String[] userIds,
      String[] groupIds) {
    if (appManagersForced && isManagers(folderProfile)) {
      ProfileInst managers = appManagers.get();
      if (isOneRemoved(managers.getAllUsers(), folderProfile.getAllUsers(), userIds) ||
          isOneRemoved(managers.getAllGroups(), folderProfile.getAllGroups(), groupIds)) {
        throw new ForbiddenRuntimeException(
            "The managers of the application cannot be removed from the managers of a folder");
      }
    }
  }

  /**
   * Locks the managers of the application in the specified selection of users and groups for
   * the given role of a folder if this role is the one of the folder managers and if the
   * application managers are forced as managers of the folders: they cannot be removed from the
   * selection. Nothing is locked otherwise.
   * @param selection the selection of users and groups to play the role in the folder.
   * @param role the name of the role.
   */
  public void lockApplicationManagers(Selection selection, String role) {
    if (appManagersForced && SilverpeasRole.ADMIN.getName().equals(role)) {
      ProfileInst managers = appManagers.get();
      selection.setLockedUsers(managers.getAllUsers());
      selection.setLockedGroups(managers.getAllGroups());
    }
  }

  /**
   * Adds the managers of the application not already in the specified profile of a folder if
   * this profile is the one of the folder managers and if the application managers are forced as
   * managers of the folders. Nothing is done otherwise.
   * @param folderProfile a profile of a folder having its own access rights.
   * @return true if some managers of the application have been added, false otherwise.
   */
  public boolean addApplicationManagers(ProfileInst folderProfile) {
    if (appManagersForced && isManagers(folderProfile)) {
      ProfileInst managers = appManagers.get();
      int count = folderProfile.getAllUsers().size() + folderProfile.getAllGroups().size();
      folderProfile.addUsers(managers.getAllUsers());
      folderProfile.addGroups(managers.getAllGroups());
      return folderProfile.getAllUsers().size() + folderProfile.getAllGroups().size() > count;
    }
    return false;
  }

  private static boolean isManagers(ProfileInst profile) {
    return SilverpeasRole.ADMIN.getName().equals(profile.getName());
  }

  private static boolean isOneRemoved(List<String> appManagerIds, List<String> actualIds,
      String[] newIds) {
    List<String> keptIds = Arrays.asList(newIds);
    return appManagerIds.stream()
        .anyMatch(id -> actualIds.contains(id) && !keptIds.contains(id));
  }
}