VYPR
Low severityNVD Advisory· Published Dec 13, 2023· Updated May 22, 2025

CVE-2023-47320

CVE-2023-47320

Description

Silverpeas Core 6.3.1 and below allows low-privileged attackers to enable Maintenance Mode, causing denial of service.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Silverpeas Core 6.3.1 and below allows low-privileged attackers to enable Maintenance Mode, causing denial of service.

CVE-2023-47320 describes an incorrect access control vulnerability in Silverpeas Core 6.3.1 and earlier versions [3]. The root cause is that a controller class, JobDomainPeasSessionController, was extending AbstractComponentSessionController instead of AbstractAdminComponentSessionController, which lacks the proper authorization checks for administrator-only functions [2]. This oversight allows any authenticated user, regardless of privilege level, to invoke the method that puts the application into Maintenance Mode.

To exploit this vulnerability, an attacker only needs a low-privilege account on the Silverpeas instance [3]. No special network position or additional authentication is required beyond a valid user session. The broken access control permits the attacker to call the administrator-only function that toggles Maintenance Mode, which is intended to be restricted to users with administrative rights [1].

The impact is a denial of service: when Maintenance Mode is enabled, the application becomes unavailable to all users, including legitimate administrators [3]. This effectively shuts down the collaborative platform, preventing any access or operations. The vulnerability is classified as a severe access control issue, as it allows a low-privilege user to perform an action that disrupts the entire service.

Silverpeas has addressed this vulnerability in a commit that changes the superclass of JobDomainPeasSessionController from AbstractComponentSessionController to AbstractAdminComponentSessionController, ensuring that only administrators can access the function [2]. Users should update to a patched version of Silverpeas Core to mitigate the risk. No workaround is documented; the software is expected to be updated to a version containing the fix.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.silverpeas.core:silverpeas-core-warMaven
< 6.3.26.3.2
org.silverpeas.core:silverpeas-core-webMaven
< 6.3.26.3.2

Affected products

3

Patches

1
fcb4a9740b6c

Bug #13812: fixing broken access control allows denial of service via maintenance mode

https://github.com/Silverpeas/Silverpeas-CoreSilverYoChaOct 27, 2023via ghsa
34 files changed · +1567 245
  • core-library/src/main/java/org/silverpeas/core/admin/service/cache/AdminCache.java+1 0 modified
    @@ -379,6 +379,7 @@ public void opAddSpace(final SpaceInst theSpace) {
                   .ifPresent(subSpaces::remove);
               subSpaces.add(theSpace);
               f.setSubSpaces(subSpaces);
    +          resetManageableSpaceIds();
             });
           }
         }
    
  • core-war/src/main/java/com/sun/portal/portletcontainer/driver/admin/PortletDeployerServlet.java+9 5 modified
    @@ -23,18 +23,19 @@
      */
     package com.sun.portal.portletcontainer.driver.admin;
     
    -import org.silverpeas.web.portlets.portal.DesktopMessages;
    -import org.silverpeas.web.portlets.portal.PropertiesContext;
    -import org.silverpeas.core.admin.user.model.UserDetail;
     import com.sun.portal.portletcontainer.admin.PortletRegistryHelper;
     import com.sun.portal.portletcontainer.admin.deployment.WebAppDeployerException;
     import com.sun.portal.portletcontainer.context.registry.PortletRegistryException;
    +import org.silverpeas.core.admin.user.model.User;
    +import org.silverpeas.core.admin.user.model.UserDetail;
    +import org.silverpeas.core.web.mvc.webcomponent.SilverpeasAuthenticatedHttpServlet;
    +import org.silverpeas.web.portlets.portal.DesktopMessages;
    +import org.silverpeas.web.portlets.portal.PropertiesContext;
     
     import javax.servlet.RequestDispatcher;
     import javax.servlet.ServletConfig;
     import javax.servlet.ServletContext;
     import javax.servlet.ServletException;
    -import javax.servlet.http.HttpServlet;
     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
     import javax.servlet.http.HttpSession;
    @@ -48,7 +49,7 @@
      * AdminServlet is a router for admin related requests like deploying/undeploying of portlets and
      * creating of portlet windows.
      */
    -public class PortletDeployerServlet extends HttpServlet {
    +public class PortletDeployerServlet extends SilverpeasAuthenticatedHttpServlet {
       private static final long serialVersionUID = 7041695476364573175L;
     
       private static final Logger logger = Logger.getLogger(PortletDeployerServlet.class
    @@ -131,6 +132,9 @@ public void doPost(HttpServletRequest request, HttpServletResponse response)
     
       public void doGetPost(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
    +    if (!User.getCurrentRequester().isAccessAdmin()) {
    +      throwHttpForbiddenError();
    +    }
     
         String language = getLanguage(request);
     
    
  • core-war/src/main/java/com/sun/portal/portletcontainer/driver/admin/UploadServlet.java+8 4 modified
    @@ -23,8 +23,6 @@
      */
     package com.sun.portal.portletcontainer.driver.admin;
     
    -import org.silverpeas.web.portlets.portal.DesktopMessages;
    -import org.silverpeas.core.admin.user.model.UserDetail;
     import com.sun.portal.portletcontainer.admin.PortletRegistryHelper;
     import com.sun.portal.portletcontainer.admin.deployment.WebAppDeployerException;
     import com.sun.portal.portletcontainer.context.registry.PortletRegistryException;
    @@ -34,12 +32,15 @@
     import org.apache.commons.fileupload.disk.SilverpeasDiskFileItemFactory;
     import org.apache.commons.fileupload.servlet.ServletFileUpload;
     import org.apache.commons.io.FilenameUtils;
    +import org.silverpeas.core.admin.user.model.User;
    +import org.silverpeas.core.admin.user.model.UserDetail;
    +import org.silverpeas.core.web.mvc.webcomponent.SilverpeasAuthenticatedHttpServlet;
    +import org.silverpeas.web.portlets.portal.DesktopMessages;
     
     import javax.servlet.RequestDispatcher;
     import javax.servlet.ServletConfig;
     import javax.servlet.ServletContext;
     import javax.servlet.ServletException;
    -import javax.servlet.http.HttpServlet;
     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
     import javax.servlet.http.HttpSession;
    @@ -53,7 +54,7 @@
     /**
      * UploadServlet is responsible for uploading the portlet war file
      */
    -public class UploadServlet extends HttpServlet {
    +public class UploadServlet extends SilverpeasAuthenticatedHttpServlet {
     
       private static final long serialVersionUID = 6041525805480787611L;
     
    @@ -76,6 +77,9 @@ public void init(ServletConfig config) throws ServletException {
       @Override
       public void doPost(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
    +    if (!User.getCurrentRequester().isAccessAdmin()) {
    +      throwHttpForbiddenError();
    +    }
     
         // Initialize DesktopMessages' Resource Bundle
         DesktopMessages.init(getLanguage(request));
    
  • core-war/src/main/java/org/silverpeas/web/importexport/control/ImportExportSessionController.java+8 2 modified
    @@ -32,7 +32,7 @@
     import org.silverpeas.core.util.MultiSilverpeasBundle;
     import org.silverpeas.core.util.ServiceProvider;
     import org.silverpeas.core.util.WAAttributeValuePair;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     
    @@ -41,7 +41,8 @@
     /**
      * @author neysseri
      */
    -public class ImportExportSessionController extends AbstractComponentSessionController {
    +public class ImportExportSessionController extends AbstractAdminComponentSessionController {
    +  private static final long serialVersionUID = -6252741698097859228L;
     
       ExportTask exportTask = null;
       Exception errorOccured = null;
    @@ -56,6 +57,11 @@ public ImportExportSessionController(MainSessionController mainSessionCtrl,
         super(mainSessionCtrl, componentContext, multilangBundle, iconBundle);
       }
     
    +  @Override
    +  public boolean isAccessGranted() {
    +    return true;
    +  }
    +
       public ImportReport processImport(String xmlFileName,
           MultiSilverpeasBundle resource) throws ImportExportException {
         ImportReport importReport = importExport.processImport(getUserDetail(), xmlFileName);
    
  • core-war/src/main/java/org/silverpeas/web/importexport/servlets/ImportExportRequestRouter.java+9 10 modified
    @@ -23,23 +23,20 @@
      */
     package org.silverpeas.web.importexport.servlets;
     
    +import org.apache.commons.fileupload.FileItem;
     import org.silverpeas.core.importexport.report.ExportPDFReport;
     import org.silverpeas.core.importexport.report.ExportReport;
     import org.silverpeas.core.importexport.report.ImportReport;
    -import org.silverpeas.web.importexport.control.ImportExportSessionController;
    -
    +import org.silverpeas.core.node.model.NodePK;
    +import org.silverpeas.core.util.MultiSilverpeasBundle;
    +import org.silverpeas.core.util.WAAttributeValuePair;
    +import org.silverpeas.core.util.file.FileRepositoryManager;
     import org.silverpeas.core.util.file.FileUploadUtil;
    -
    +import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    -import org.silverpeas.core.util.MultiSilverpeasBundle;
    -import org.silverpeas.core.util.file.FileRepositoryManager;
    -import org.silverpeas.core.util.WAAttributeValuePair;
    -import org.silverpeas.core.node.model.NodePK;
    -
    -import org.apache.commons.fileupload.FileItem;
    -import org.silverpeas.core.web.http.HttpRequest;
    +import org.silverpeas.web.importexport.control.ImportExportSessionController;
     
     import java.io.File;
     import java.util.List;
    @@ -83,8 +80,10 @@ public String getDestination(String function, ImportExportSessionController impo
         String destination = "";
         try {
           if (function.startsWith("Main")) {
    +        importExportSC.checkAdminAccessOnly();
             destination = "/importExportPeas/jsp/welcome.jsp";
           } else if ("Import".equals(function)) {
    +        importExportSC.checkAdminAccessOnly();
             File file = null;
             List<FileItem> items = request.getFileItems();
             for (FileItem item : items) {
    
  • core-war/src/main/java/org/silverpeas/web/jobdomain/control/JobDomainPeasSessionController.java+159 2 modified
    @@ -88,7 +88,7 @@
     import org.silverpeas.core.util.logging.SilverLogger;
     import org.silverpeas.core.web.authentication.LoginServlet;
     import org.silverpeas.core.web.http.HttpRequest;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.web.mvc.webcomponent.WebMessager;
    @@ -116,14 +116,15 @@
     import static org.silverpeas.core.admin.domain.DomainDriverManagerProvider.getCurrentDomainDriverManager;
     import static org.silverpeas.core.personalization.service.PersonalizationServiceProvider.getPersonalizationService;
     import static org.silverpeas.core.util.ResourceLocator.getSettingBundle;
    +import static org.silverpeas.core.util.StringUtil.defaultStringIfNotDefined;
     import static org.silverpeas.core.util.StringUtil.isDefined;
     
     /**
      * Class declaration
      *
      * @author
      */
    -public class JobDomainPeasSessionController extends AbstractComponentSessionController {
    +public class JobDomainPeasSessionController extends AbstractAdminComponentSessionController {
     
       public static final String REPLACE_RIGHTS = "1";
       public static final String ADD_RIGHTS = "2";
    @@ -179,6 +180,15 @@ public JobDomainPeasSessionController(MainSessionController mainSessionCtrl,
             .getString("customersTemplatePath"));
       }
     
    +  /**
    +   * Dedicated for tests
    +   */
    +  protected JobDomainPeasSessionController(final MainSessionController controller,
    +      final ComponentContext context, final String localizedMessagesBundleName,
    +      final String iconFileName, final String settingsFileName) {
    +    super(controller, context, localizedMessagesBundleName, iconFileName, settingsFileName);
    +  }
    +
       public int getMinLengthLogin() {
         return JobDomainSettings.m_MinLengthLogin;
       }
    @@ -187,11 +197,95 @@ public boolean isUserAddingAllowedForGroupManager() {
         return JobDomainSettings.m_UserAddingAllowedForGroupManagers;
       }
     
    +  @Override
       public boolean isAccessGranted() {
         return getUserDetail().isAccessAdmin() || getUserDetail().isAccessDomainManager() ||
             isOnlySpaceManager() || !getUserManageableGroupIds().isEmpty();
       }
     
    +  public void checkDomainAccessGranted(final String domainId) {
    +    checkDomainAccessGranted(domainId, true);
    +  }
    +
    +  public void checkCurrentDomainAccessGranted(final boolean readOnly) {
    +    final String domainId = Optional.ofNullable(getTargetDomain()).map(Domain::getId).orElse(null);
    +    checkDomainAccessGranted(domainId, readOnly);
    +  }
    +
    +  void checkDomainAccessGranted(final String domainId, final boolean readOnly) {
    +    checkAccessGranted(domainId, new DomainAccessContext(), readOnly);
    +  }
    +
    +  public void checkUserAccessGranted(final String userId, final boolean readOnly) {
    +    final User user = getUserDetail(userId);
    +    if (user == null || getTargetDomain() == null ||
    +        !getTargetDomain().getId().equals(user.getDomainId()) ||
    +        getTargetDomain().getId().equals(Domain.MIXED_DOMAIN_ID)) {
    +      throwForbiddenError();
    +    } else {
    +      checkAccessGranted(user.getDomainId(), new UserAccessContext(user), readOnly);
    +    }
    +  }
    +
    +  public void checkGroupAccessGranted(final String groupId, final boolean readOnly) {
    +    final Group group = getOrganisationController().getGroup(groupId);
    +    if (group == null) {
    +      throwForbiddenError();
    +    } else {
    +      final String domainId = defaultStringIfNotDefined(group.getDomainId(), Domain.MIXED_DOMAIN_ID);
    +      if (getTargetDomain() == null || !getTargetDomain().getId().equals(domainId)) {
    +        throwForbiddenError();
    +      } else {
    +        checkAccessGranted(domainId, new GroupAccessContext(group), readOnly);
    +      }
    +    }
    +  }
    +
    +  private <T> void checkAccessGranted(final String domainId, final AccessContext<T> accessContext,
    +      final boolean readOnly) {
    +    final UserDetail ud = getUserDetail();
    +    final boolean granted = ud.isAccessAdmin() || isAccessGrantedOnNotFullAdminAccess(
    +        defaultStringIfNotDefined(domainId, Domain.MIXED_DOMAIN_ID), accessContext, readOnly);
    +    if (!granted) {
    +      throwForbiddenError();
    +    }
    +  }
    +
    +  private <T> boolean isAccessGrantedOnNotFullAdminAccess(final String domainId,
    +      final AccessContext<T> accessContext, final boolean readOnly) {
    +    final UserDetail ud = getUserDetail();
    +    final boolean equalsUserDomain = domainId.equals(ud.getDomainId());
    +    boolean granted = ud.isAccessDomainManager() && equalsUserDomain;
    +    if (!granted && (readOnly || accessContext.getType().isGroup())) {
    +      Stream<Pair<Group, String>> groupStream = getUserManageableGroups().stream()
    +          .map(g -> Pair.of(g, defaultStringIfNotDefined(g.getDomainId(), Domain.MIXED_DOMAIN_ID)))
    +          .filter(p -> !ud.isDomainAdminRestricted() ||
    +              p.getSecond().equals(Domain.MIXED_DOMAIN_ID) ||
    +              p.getSecond().equals(ud.getDomainId()));
    +      if (!readOnly && accessContext.getType().isGroup()) {
    +        final Group aimedGroup = (Group) accessContext.getResource();
    +        if (aimedGroup == null) {
    +          groupStream = groupStream.filter(g -> false);
    +        } else {
    +          final List<String> aimedGroupPath = Stream.concat(
    +              Stream.of(aimedGroup.getId()),
    +              Optional.of(aimedGroup)
    +                  .filter(g -> isDefined(g.getSuperGroupId()))
    +                  .stream()
    +                  .flatMap(g -> getOrganisationController().getPathToGroup(g.getId()).stream()))
    +              .collect(toList());
    +          groupStream = groupStream.filter(p -> aimedGroupPath.contains(p.getFirst().getId()));
    +        }
    +      }
    +      granted = groupStream.map(Pair::getSecond).anyMatch(domainId::equals);
    +      if (!granted && readOnly) {
    +        granted = (equalsUserDomain || Domain.MIXED_DOMAIN_ID.equals(domainId)) &&
    +            (isOnlySpaceManager() || isCommunityManager());
    +      }
    +    }
    +    return granted;
    +  }
    +
       public void setRefreshDomain(boolean refreshDomain) {
         this.refreshDomain = refreshDomain;
       }
    @@ -200,6 +294,9 @@ public void setRefreshDomain(boolean refreshDomain) {
        * USER functions
        */
       public void setTargetUser(String userId) {
    +    if (isDefined(userId)) {
    +      checkUserAccessGranted(userId, true);
    +    }
         targetUserId = userId;
         processIndex(targetUserId);
       }
    @@ -1455,6 +1552,7 @@ public void setTargetDomain(String domainId) {
           targetDomain = null;
           targetDomainId = "";
         } else {
    +      checkDomainAccessGranted(domainId);
           List<String> manageableGroupIds = null;
           targetDomainId = domainId;
           if (isOnlyGroupManager()) {
    @@ -2409,4 +2507,63 @@ public boolean isOnlySpaceManager() {
             !isOnlyGroupManager() && !isManagerOfCurrentDomain() &&
             ArrayUtil.isNotEmpty(getUserManageableSpaceIds());
       }
    +
    +  /**
    +   * In order to check the user granted access to domain services, the context of use MUST be set.
    +   * <p>
    +   *   This context is an implementation of this abstraction.
    +   * </p>
    +   * @param <T>
    +   */
    +  private static abstract class AccessContext<T> {
    +    private final AccessContextType type;
    +    private final T resource;
    +
    +    private AccessContext(final AccessContextType accessContext, final T resource) {
    +      this.type = accessContext;
    +      this.resource = resource;
    +    }
    +
    +    public AccessContextType getType() {
    +      return type;
    +    }
    +
    +    public T getResource() {
    +      return resource;
    +    }
    +
    +    enum AccessContextType {
    +      DOMAIN, GROUP, USER;
    +      boolean isGroup() {
    +        return this == GROUP;
    +      }
    +    }
    +  }
    +
    +  /**
    +   * Context of a domain access.
    +   */
    +  private static class DomainAccessContext extends AccessContext<Void> {
    +    private DomainAccessContext() {
    +      super(AccessContextType.DOMAIN, null);
    +    }
    +  }
    +
    +  /**
    +   * Context of user services access.
    +   */
    +  private static class UserAccessContext extends AccessContext<User> {
    +    private UserAccessContext(final User user) {
    +      super(AccessContextType.USER, user);
    +    }
    +  }
    +
    +  /**
    +   * Context of group services access.
    +   */
    +  private static class GroupAccessContext extends AccessContext<Group> {
    +    private GroupAccessContext(final Group group) {
    +      super(AccessContextType.GROUP, group);
    +    }
    +  }
     }
    \ No newline at end of file
    
  • core-war/src/main/java/org/silverpeas/web/jobdomain/servlets/JobDomainPeasRequestRouter.java+48 18 modified
    @@ -52,7 +52,7 @@
     import org.silverpeas.core.web.http.RequestParameterDecoder;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     import org.silverpeas.core.web.selection.Selection;
     import org.silverpeas.web.jobdomain.JobDomainPeasException;
     import org.silverpeas.web.jobdomain.JobDomainSettings;
    @@ -72,7 +72,7 @@
     import static org.silverpeas.web.jobdomain.servlets.RemovedUserUIEntity.convertRemovedUserList;
     
     public class JobDomainPeasRequestRouter extends
    -    ComponentRequestRouter<JobDomainPeasSessionController> {
    +    AdminComponentRequestRouter<JobDomainPeasSessionController> {
     
       private static final long serialVersionUID = 1L;
     
    @@ -120,6 +120,8 @@ public class JobDomainPeasRequestRouter extends
       private static final String DISPLAY_REMOVED_USERS_DEST = "displayRemovedUsers";
       private static final String DOMAIN_USER_FILTER_MANAGEMENT_DEST = "domainUserFilterManagement.jsp";
       private static final String IS_ONLY_SPACE_MANAGER_ATTR = "isOnlySpaceManager";
    +  private static final String WRITE_OPERATION_PARTS =
    +      "(?i)^.*(create|update|modify|delete|remove|block|activate|import|synchro).*$";
     
       @Override
       public JobDomainPeasSessionController createComponentSessionController(
    @@ -147,15 +149,12 @@ public String getSessionControlBeanName() {
        * "/almanach/jsp/almanach.jsp?flag=user")
        */
       @Override
    -  public String getDestination(String function, JobDomainPeasSessionController jobDomainSC,
    +  public String getAdminDestination(String function, JobDomainPeasSessionController jobDomainSC,
           HttpRequest request) {
         String destination = "";
     
     
         try {
    -      if (!jobDomainSC.isAccessGranted()) {
    -        throw new JobDomainPeasException("Bad right for user {0}", jobDomainSC.getUserId());
    -      }
           // 1) Performs the action
           // ----------------------
           if (function.startsWith("selectUserOrGroup")) {
    @@ -176,6 +175,7 @@ public String getDestination(String function, JobDomainPeasSessionController job
           }
     
           if ("blankUsers".equals(function)) {
    +        jobDomainSC.checkCurrentDomainAccessGranted(false);
             final List<String> userIds = new ArrayList<>();
             request.mergeSelectedItemsInto(userIds);
             if (!userIds.isEmpty()) {
    @@ -197,20 +197,23 @@ public String getDestination(String function, JobDomainPeasSessionController job
             jobDomainSC.setTargetUser(user.getId());
             destination = USER_CONTENT_DEST;
           } else if ("restoreUsers".equals(function)) {
    +        jobDomainSC.checkCurrentDomainAccessGranted(false);
             final List<String> userIds = new ArrayList<>();
             request.mergeSelectedItemsInto(userIds);
             for (final String u : userIds) {
               jobDomainSC.restoreUser(u);
             }
             destination = getDestination(DISPLAY_REMOVED_USERS_DEST, jobDomainSC, request);
           } else if ("deleteUsers".equals(function)) {
    +        jobDomainSC.checkCurrentDomainAccessGranted(false);
             final List<String> userIds = new ArrayList<>();
             request.mergeSelectedItemsInto(userIds);
             for (final String u : userIds) {
               jobDomainSC.deleteUser(u);
             }
             destination = getDestination(DISPLAY_REMOVED_USERS_DEST, jobDomainSC, request);
           } else if ("restoreGroups".equals(function)) {
    +        jobDomainSC.checkCurrentDomainAccessGranted(false);
             final List<String> groupIds = new ArrayList<>();
             request.mergeSelectedItemsInto(groupIds);
             boolean refreshDomainNav = false;
    @@ -222,6 +225,7 @@ public String getDestination(String function, JobDomainPeasSessionController job
             }
             destination = getDestination(DISPLAY_REMOVED_GROUPS_DEST, jobDomainSC, request);
           } else if ("deleteGroups".equals(function)) {
    +        jobDomainSC.checkCurrentDomainAccessGranted(false);
             final List<String> groupIds = new ArrayList<>();
             request.mergeSelectedItemsInto(groupIds);
             for (final String group : groupIds) {
    @@ -231,6 +235,14 @@ public String getDestination(String function, JobDomainPeasSessionController job
           } else if (function.startsWith("user")) {
             // USER Actions --------------------------------------------
             String userId = request.getParameter("Iduser");
    +        final boolean readOperation = !function.matches(WRITE_OPERATION_PARTS);
    +        if (isDefined(userId)) {
    +          jobDomainSC.checkUserAccessGranted(userId, readOperation);
    +        } else if (jobDomainSC.getTargetUserDetail() != null) {
    +          jobDomainSC.checkUserAccessGranted(jobDomainSC.getTargetUserDetail().getId(), readOperation);
    +        } else {
    +          jobDomainSC.checkCurrentDomainAccessGranted(readOperation);
    +        }
             if (function.startsWith(USER_CONTENT_FCT)) {
               if (isDefined(userId)) {
                 jobDomainSC.setTargetUser(userId);
    @@ -406,44 +418,55 @@ public String getDestination(String function, JobDomainPeasSessionController job
             boolean bHaveToRefreshDomain = false;
     
             jobDomainSC.setTargetUser(null);
    +        String groupId = request.getParameter(IDGROUP_PARAM);
    +        final boolean readOperation = !function.matches(WRITE_OPERATION_PARTS);
    +        if (isDefined(groupId)) {
    +          jobDomainSC.checkGroupAccessGranted(groupId, readOperation);
    +        } else if (jobDomainSC.getTargetGroup() != null) {
    +          jobDomainSC.checkGroupAccessGranted(jobDomainSC.getTargetGroup().getId(), readOperation);
    +        } else {
    +          jobDomainSC.checkCurrentDomainAccessGranted(readOperation);
    +        }
     
             // Browse functions
             // ----------------
             if (function.startsWith(GROUP_CONTENT_FCT)) {
    -          String groupId = request.getParameter(IDGROUP_PARAM);
               if (isDefined(groupId)) {
                 jobDomainSC.goIntoGroup(groupId);
               }
             } else if (function.startsWith("groupExport.txt")) {
    -          String groupId = request.getParameter(IDGROUP_PARAM);
               if (isDefined(groupId)) {
    -            jobDomainSC.goIntoGroup(request.getParameter(IDGROUP_PARAM));
    +            jobDomainSC.goIntoGroup(groupId);
                 destination = "exportgroup.jsp";
               }
             } else if (function.startsWith("groupReturn")) {
    -          jobDomainSC.returnIntoGroup(request.getParameter(IDGROUP_PARAM));
    +          jobDomainSC.returnIntoGroup(groupId);
             } else if (function.startsWith("groupSet")) {
               jobDomainSC.returnIntoGroup(null);
    -          jobDomainSC.goIntoGroup(request.getParameter(IDGROUP_PARAM));
    +          jobDomainSC.goIntoGroup(groupId);
             } else if (function.startsWith("groupCreate")) {
    -          bHaveToRefreshDomain = jobDomainSC.createGroup(request.getParameter("Idparent"),
    +          final String parentGroupId = request.getParameter("Idparent");
    +          if (isDefined(parentGroupId)) {
    +            jobDomainSC.checkGroupAccessGranted(parentGroupId, false);
    +          }
    +          bHaveToRefreshDomain = jobDomainSC.createGroup(parentGroupId,
                   WebEncodeHelper.htmlStringToJavaString(request.getParameter(GROUP_NAME_PARAM)),
                   WebEncodeHelper.htmlStringToJavaString(request.getParameter("groupDescription")),
                   request.getParameter("groupRule"));
             } else if (function.startsWith("groupUpdate")) {
    -          bHaveToRefreshDomain = jobDomainSC.modifyGroup(request.getParameter(IDGROUP_PARAM),
    +          bHaveToRefreshDomain = jobDomainSC.modifyGroup(groupId,
                   WebEncodeHelper.htmlStringToJavaString(request.getParameter(GROUP_NAME_PARAM)),
                   WebEncodeHelper.htmlStringToJavaString(request.getParameter("groupDescription")),
                   request.getParameter("groupRule"));
             } else if (function.startsWith("groupAddRemoveUsers")) {
               bHaveToRefreshDomain = jobDomainSC
                   .updateGroupSubUsers(jobDomainSC.getTargetGroup().getId(), jobDomainSC.getSelectedUsersIds());
             } else if (function.startsWith("groupRemove")) {
    -          bHaveToRefreshDomain = jobDomainSC.removeGroup(request.getParameter(IDGROUP_PARAM));
    +          bHaveToRefreshDomain = jobDomainSC.removeGroup(groupId);
             } else if (function.startsWith("groupDelete")) {
    -          bHaveToRefreshDomain = jobDomainSC.deleteGroup(request.getParameter(IDGROUP_PARAM));
    +          bHaveToRefreshDomain = jobDomainSC.deleteGroup(groupId);
             } else if (function.startsWith("groupSynchro")) {
    -          final Optional<Group> synchronizedGroup = jobDomainSC.synchroGroup(request.getParameter(IDGROUP_PARAM));
    +          final Optional<Group> synchronizedGroup = jobDomainSC.synchroGroup(groupId);
               if (synchronizedGroup.isPresent()) {
                 final Group group = synchronizedGroup.get();
                 if (group.isRemovedState()) {
    @@ -453,7 +476,7 @@ public String getDestination(String function, JobDomainPeasSessionController job
                 }
               }
             } else if (function.startsWith("groupUnSynchro")) {
    -          bHaveToRefreshDomain = jobDomainSC.unsynchroGroup(request.getParameter(IDGROUP_PARAM));
    +          bHaveToRefreshDomain = jobDomainSC.unsynchroGroup(groupId);
             } else if (function.startsWith("groupImport")) {
               bHaveToRefreshDomain = jobDomainSC.importGroup(WebEncodeHelper.htmlStringToJavaString(request.getParameter(
                   GROUP_NAME_PARAM)));
    @@ -480,7 +503,7 @@ public String getDestination(String function, JobDomainPeasSessionController job
     
               destination = getDestination("groupManagersView", jobDomainSC, request);
             } else if ("groupOpen".equals(function)) {
    -          String groupId = request.getParameter("groupId");
    +          groupId = request.getParameter("groupId");
     
               if (jobDomainSC.isAccessGranted() || jobDomainSC.isGroupManagerOnGroup(groupId)) {
                 OrganizationController orgaController = jobDomainSC.getOrganisationController();
    @@ -529,6 +552,12 @@ public String getDestination(String function, JobDomainPeasSessionController job
             // DOMAIN Actions --------------------------------------------
           } else if (function.startsWith(DOMAIN_ATTR)) {
             jobDomainSC.setTargetUser(null);
    +        final boolean writeOperation = function.matches(WRITE_OPERATION_PARTS);
    +        if (writeOperation) {
    +          jobDomainSC.checkAdminAccessOnly();
    +        } else if (jobDomainSC.getTargetDomain() != null) {
    +          jobDomainSC.checkCurrentDomainAccessGranted(true);
    +        }
             if (function.startsWith("domainModifyUserFilter")) {
               destination = handleUserFilterModification(jobDomainSC, request);
             } else if (function.startsWith("domainGoTo")) {
    @@ -811,6 +840,7 @@ public String getDestination(String function, JobDomainPeasSessionController job
           } else if ("SelectRightsUserOrGroup".equals(function)) {
             destination = jobDomainSC.initSelectionRightsUserOrGroup();
           } else if ("AssignSameRights".equals(function)) {
    +        jobDomainSC.checkAdminAccessOnly();
             if (!jobDomainSC.isRightCopyReplaceEnabled()) {
               throwHttpForbiddenError();
             }
    
  • core-war/src/main/java/org/silverpeas/web/jobmanager/control/JobManagerPeasSessionController.java+35 14 modified
    @@ -28,7 +28,7 @@
     import org.silverpeas.core.util.ArrayUtil;
     import org.silverpeas.core.util.logging.SilverLogger;
     import org.silverpeas.core.wbe.WbeSettings;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.web.jobmanager.JobManagerService;
    @@ -48,7 +48,7 @@
      *
      * @author
      */
    -public class JobManagerPeasSessionController extends AbstractComponentSessionController {
    +public class JobManagerPeasSessionController extends AbstractAdminComponentSessionController {
     
       private Map<String, JobManagerService> services = null;
       private String idCurrentServiceActif = null;
    @@ -144,7 +144,7 @@ private void initServices() {
         JobManagerService jSTAT4 = new JobManagerService("34", "JSTAT4", LEVEL_OPERATION, webContext
             + getURL(CMP_SILVERSTATISTICSPEAS, null, null) + "ViewPDCAccess", null, false);
     
    -    boolean kmServiceAllowed = false;
    +    boolean pdcServicesAdded = false;
         int nbServices = 0;
     
         JobManagerSettings jobManagerSettings = JobManagerSettings.get();
    @@ -162,7 +162,7 @@ private void initServices() {
             services.put(jKM.getId(), jKM);
             services.put(jKM1.getId(), jKM1);
             services.put(jKM2.getId(), jKM2);
    -        kmServiceAllowed = true;
    +        pdcServicesAdded = true;
           }
     
           if (jobManagerSettings.isToolSpecificAuthentVisible() ||
    @@ -241,7 +241,7 @@ private void initServices() {
             services.put(jKM1.getId(), jKM1);
             services.put(jKM2.getId(), jKM2);
     
    -        kmServiceAllowed = true;
    +        pdcServicesAdded = true;
           }
     
           services.put(jSTAT.getId(), jSTAT);
    @@ -255,7 +255,7 @@ private void initServices() {
           services.put(jKM1.getId(), jKM1);
           services.put(jKM2.getId(), jKM2);
     
    -      kmServiceAllowed = true;
    +      pdcServicesAdded = true;
         } else if (getUserDetail().isAccessDomainManager() || !getUserManageableGroupIds().isEmpty()) {
           String[] id1 = { jdp.getId() };
           jDesigner = new JobManagerService("1", "JD", LEVEL_SERVICE, null, id1, false);
    @@ -265,15 +265,9 @@ private void initServices() {
           services.put(jdp.getId(), jdp);
         }
     
    -    boolean isPDCManager = false;
    -
    -    try {
    -      isPDCManager = PdcManager.get().isUserManager(getUserId());
    -    } catch (PdcException e) {
    -      SilverLogger.getLogger(this).error(e);
    -    }
    +    final boolean isPDCManager = isPDCManager();
     
    -    if (!kmServiceAllowed && isPDCManager) {
    +    if (!pdcServicesAdded && isPDCManager) {
           String[] id1 = { jKM1.getId() };
           jKM = new JobManagerService(Integer.toString(nbServices + 1), "JKM",
               LEVEL_SERVICE, null, id1, false);
    @@ -283,6 +277,33 @@ private void initServices() {
         }
       }
     
    +  @Override
    +  public boolean isAccessGranted() {
    +    boolean accessGranted =
    +        getUserDetail().isAccessAdmin() || getUserManageableSpaceIds().length != 0;
    +    if (!accessGranted) {
    +      accessGranted =
    +          getUserDetail().isAccessDomainManager() || !getUserManageableGroupIds().isEmpty();
    +    }
    +    if (!accessGranted) {
    +      JobManagerSettings jobManagerSettings = JobManagerSettings.get();
    +      accessGranted = jobManagerSettings.isKMVisible() &&
    +          (getUserDetail().isAccessPdcManager() || isPDCManager());
    +    }
    +    return accessGranted;
    +  }
    +
    +  private boolean isPDCManager() {
    +    boolean isPDCManager = false;
    +
    +    try {
    +      isPDCManager = PdcManager.get().isUserManager(getUserId());
    +    } catch (PdcException e) {
    +      SilverLogger.getLogger(this).error(e);
    +    }
    +    return isPDCManager;
    +  }
    +
       // retourne les services de niveau level
       // 0 => niveau Service
       // 1 => opération
    
  • core-war/src/main/java/org/silverpeas/web/jobmanager/servlets/JobManagerPeasRequestRouter.java+4 3 modified
    @@ -26,7 +26,7 @@
     import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     import org.silverpeas.web.jobmanager.JobManagerService;
     import org.silverpeas.web.jobmanager.control.JobManagerPeasSessionController;
     
    @@ -40,7 +40,7 @@
      * @author
      */
     public class JobManagerPeasRequestRouter extends
    -    ComponentRequestRouter<JobManagerPeasSessionController> {
    +    AdminComponentRequestRouter<JobManagerPeasSessionController> {
     
       private static final long serialVersionUID = -2003485584890163789L;
       private static final String OPERATION_ACTION = "Operation";
    @@ -78,7 +78,8 @@ public String getSessionControlBeanName() {
        * @return The complete destination URL for a forward (ex :
        * "/almanach/jsp/almanach.jsp?flag=user")
        */
    -  public String getDestination(String function, JobManagerPeasSessionController jobManagerSC,
    +  @Override
    +  public String getAdminDestination(String function, JobManagerPeasSessionController jobManagerSC,
           HttpRequest request) {
         String destination;
         try {
    
  • core-war/src/main/java/org/silverpeas/web/jobstartpage/control/JobStartPagePeasSessionController.java+23 36 modified
    @@ -51,7 +51,6 @@
     import org.silverpeas.core.clipboard.ClipboardSelection;
     import org.silverpeas.core.contribution.template.publication.PublicationTemplateException;
     import org.silverpeas.core.contribution.template.publication.PublicationTemplateManager;
    -import org.silverpeas.core.exception.SilverpeasRuntimeException;
     import org.silverpeas.core.i18n.I18NHelper;
     import org.silverpeas.core.template.SilverpeasTemplate;
     import org.silverpeas.core.template.SilverpeasTemplateFactory;
    @@ -69,7 +68,7 @@
     import org.silverpeas.core.util.logging.SilverLogger;
     import org.silverpeas.core.util.memory.MemoryUnit;
     import org.silverpeas.core.web.look.SilverpeasLook;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.web.selection.Selection;
    @@ -93,7 +92,7 @@
      *
      * @author
      */
    -public class JobStartPagePeasSessionController extends AbstractComponentSessionController {
    +public class JobStartPagePeasSessionController extends AbstractAdminComponentSessionController {
     
       public static final int SCOPE_BACKOFFICE = 0;
       public static final int SCOPE_FRONTOFFICE = 1;
    @@ -130,13 +129,24 @@ public JobStartPagePeasSessionController(MainSessionController mainSessionCtrl,
             JobStartPagePeasSettings.CUSTOMERS_TEMPLATE_PATH);
       }
     
    +  /**
    +   * Dedicated for tests
    +   */
    +  public JobStartPagePeasSessionController(final MainSessionController controller,
    +      final ComponentContext context, final String localizedMessagesBundleName,
    +      final String iconFileName, final String settingsFileName) {
    +    super(controller, context, localizedMessagesBundleName, iconFileName, settingsFileName);
    +    adminController = null;
    +  }
    +
       // Init at first entry
       public void init() {
         m_NavBarMgr.initWithUser(this, getUserDetail());
       }
     
    -  public boolean isUserAdmin() {
    -    return getUserDetail().isAccessAdmin();
    +  @Override
    +  public boolean isAccessGranted() {
    +    return isAccessGranted(getManagedSpaceId(), getManagedInstanceId(), true);
       }
     
       // method du spaceInst
    @@ -149,6 +159,7 @@ public SpaceInst getSpaceInstById() {
     
       public void setManagedSpaceId(String sId, boolean isManagedSpaceRoot) {
         String spaceId = getShortSpaceId(sId);
    +    checkAccessGranted(spaceId, null, true);
         m_ManagedSpaceId = spaceId;
         m_isManagedSpaceRoot = isManagedSpaceRoot;
     
    @@ -250,6 +261,7 @@ public void refreshCurrentSpaceCache() {
       }
     
       public void setManagedInstanceId(String sId) {
    +    checkAccessGranted(null, sId, true);
         m_ManagedInstanceId = sId;
         setScope(SCOPE_BACKOFFICE);
       }
    @@ -263,10 +275,6 @@ public String getManagedInstanceId() {
         return m_ManagedInstanceId;
       }
     
    -  public boolean isComponentManageable(String componentId) {
    -    return getOrganisationController().isComponentManageable(componentId, getUserId());
    -  }
    -
       public void setManagedProfile(ProfileInst sProfile) {
         m_ManagedProfile = sProfile;
     
    @@ -353,25 +361,6 @@ public SpaceInst[] getBrotherSpaces(boolean isNew) {
         return m_BrothersSpaces;
       }
     
    -  // Get spaces "manageable" by the current user (ie spaces in maintenance or current space)
    -  public SpaceInst[] getUserManageableSpacesIds() {
    -    List<SpaceInst> vManageableSpaces = new ArrayList<SpaceInst>();
    -    String[] sids = getUserManageableSpaceIds();
    -    SpaceInst currentSpace = getSpaceInstById();
    -    String currentSpaceId = (currentSpace == null) ? "-1" : currentSpace.getId();
    -
    -    for (String sid : sids) {
    -      if (isSpaceInMaintenance(sid.substring(2)) || sid.equals(currentSpaceId)) {
    -        vManageableSpaces.add(adminController.getSpaceInstById(sid));
    -      }
    -    }
    -
    -    SpaceInst[] aManageableSpaces = vManageableSpaces.toArray(
    -        new SpaceInst[vManageableSpaces.size()]);
    -    Arrays.sort(aManageableSpaces, Comparator.comparing(SpaceInst::getOrderNum));
    -    return aManageableSpaces;
    -  }
    -
       public void setSpacePlace(String idSpaceBefore) {
         int orderNum = 0;
         int i;
    @@ -1107,6 +1096,7 @@ public void cutComponent(String id) throws ClipboardException {
       }
     
       private void copyOrCutComponent(String id, boolean cut) throws ClipboardException {
    +    checkAccessGranted(null, id, false);
         ComponentInst componentInst = getComponentInst(id);
         ComponentSelection compoSelect = new ComponentSelection(componentInst);
         compoSelect.setCutted(cut);
    @@ -1122,6 +1112,7 @@ public void cutSpace(String id) throws ClipboardException {
       }
     
       private void copyOrCutSpace(String id, boolean cut) throws ClipboardException {
    +    checkAccessGranted(id, null, false);
         SpaceInst space = getSpaceInstById(id);
         SpaceSelection spaceSelect = new SpaceSelection(space);
         spaceSelect.setCutted(cut);
    @@ -1135,6 +1126,7 @@ private void copyOrCutSpace(String id, boolean cut) throws ClipboardException {
        * @throws JobStartPagePeasException
        */
       public void paste(Map<String, String> options) throws ClipboardException, JobStartPagePeasException {
    +    checkAccessGranted(getManagedSpaceId(), getManagedInstanceId(), false);
         try {
           Collection<ClipboardSelection> clipObjects = getClipboardSelectedObjects();
           boolean refreshCache = false;
    @@ -1170,8 +1162,7 @@ public void paste(Map<String, String> options) throws ClipboardException, JobSta
           }
         } catch (Exception e) {
           m_NavBarMgr.resetAllCache();
    -      throw new JobStartPagePeasException("JobStartPagePeasSessionController.paste()",
    -          SilverpeasRuntimeException.ERROR, "jobStartPagePeas.EX_PASTE_ERROR", e);
    +      throw new JobStartPagePeasException(e);
         }
         clipboardPasteDone();
       }
    @@ -1206,8 +1197,7 @@ public Set<String> getCopiedComponents() throws JobStartPagePeasException {
             }
           }
         } catch (Exception e) {
    -      throw new JobStartPagePeasException("JobStartPagePeasSessionController.getCopiedComponents()",
    -          SilverpeasRuntimeException.ERROR, "jobStartPagePeas.EX_PASTE_ERROR", e);
    +      throw new JobStartPagePeasException(e);
         }
         return copiedComponents;
       }
    @@ -1228,8 +1218,7 @@ private void pasteComponent(PasteDetail pasteDetail) throws JobStartPagePeasExce
             refreshCurrentSpaceCache();
           }
         } catch (Exception e) {
    -      throw new JobStartPagePeasException("JobStartPagePeasSessionController.pasteComponent()",
    -          SilverpeasRuntimeException.ERROR, "jobStartPagePeas.EX_PASTE_ERROR",
    +      throw new JobStartPagePeasException(
               "componentId = " + pasteDetail.getFromComponentId() + " in space " + getManagedSpaceId(),
               e);
         }
    @@ -1252,8 +1241,6 @@ private void pasteSpace(PasteDetail pasteDetail) throws JobStartPagePeasExceptio
           }
         } catch (Exception e) {
           throw new JobStartPagePeasException(
    -          "JobStartPagePeasSessionController.pasteSpace()",
    -          SilverpeasRuntimeException.ERROR, "jobStartPagePeas.EX_PASTE_ERROR",
               "spaceId = " + pasteDetail.getFromSpaceId() + " in space " + getManagedSpaceId(), e);
         }
       }
    
  • core-war/src/main/java/org/silverpeas/web/jobstartpage/JobStartPagePeasException.java+8 58 modified
    @@ -30,75 +30,25 @@
     
     package org.silverpeas.web.jobstartpage;
     
    -import org.silverpeas.core.exception.SilverpeasException;
    +import org.silverpeas.core.SilverpeasException;
     
     /**
    - * Class declaration
    - * @author
    + * Exception when an error is raised from a Silverpeas space and component related job.
      */
     public class JobStartPagePeasException extends SilverpeasException {
     
       private static final long serialVersionUID = 7820592552517371949L;
     
    -  /**
    -   * Constructor declaration
    -   * @param callingClass
    -   * @param errorLevel
    -   * @param message
    -   *
    -   */
    -  public JobStartPagePeasException(String callingClass, int errorLevel,
    -      String message) {
    -    super(callingClass, errorLevel, message);
    -  }
    -
    -  /**
    -   * Constructor declaration
    -   * @param callingClass
    -   * @param errorLevel
    -   * @param message
    -   * @param extraParams
    -   *
    -   */
    -  public JobStartPagePeasException(String callingClass, int errorLevel,
    -      String message, String extraParams) {
    -    super(callingClass, errorLevel, message, extraParams);
    -  }
     
    -  /**
    -   * Constructor declaration
    -   * @param callingClass
    -   * @param errorLevel
    -   * @param message
    -   * @param nested
    -   *
    -   */
    -  public JobStartPagePeasException(String callingClass, int errorLevel,
    -      String message, Exception nested) {
    -    super(callingClass, errorLevel, message, nested);
    +  public JobStartPagePeasException(final String message, String ... parameters) {
    +    super(message, parameters);
       }
     
    -  /**
    -   * Constructor declaration
    -   * @param callingClass
    -   * @param errorLevel
    -   * @param message
    -   * @param extraParams
    -   * @param nested
    -   *
    -   */
    -  public JobStartPagePeasException(String callingClass, int errorLevel,
    -      String message, String extraParams, Exception nested) {
    -    super(callingClass, errorLevel, message, extraParams, nested);
    +  public JobStartPagePeasException(final String message, final Throwable cause) {
    +    super(message, cause);
       }
     
    -  /**
    -   * Method declaration
    -   * @return
    -   *
    -   */
    -  public String getModule() {
    -    return "jobStartPagePeas";
    +  public JobStartPagePeasException(final Throwable cause) {
    +    super(cause);
       }
    -
     }
    
  • core-war/src/main/java/org/silverpeas/web/jobstartpage/servlets/JobStartPagePeasRequestRouter.java+19 19 modified
    @@ -43,7 +43,7 @@
     import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     import org.silverpeas.core.web.selection.Selection;
     import org.silverpeas.web.jobstartpage.JobStartPagePeasSettings;
     import org.silverpeas.web.jobstartpage.NavBarJsonEncoder;
    @@ -61,7 +61,8 @@
     import static java.util.Optional.ofNullable;
     import static org.silverpeas.core.util.JSONCodec.encodeObject;
     
    -public class JobStartPagePeasRequestRouter extends ComponentRequestRouter<JobStartPagePeasSessionController> {
    +public class JobStartPagePeasRequestRouter extends
    +    AdminComponentRequestRouter<JobStartPagePeasSessionController> {
     
       private static final long serialVersionUID = 3751632991093466433L;
       private static final String WELCOME_SPACE_ADMIN_TEMPLATE_FILE = "/space/welcome_space_admin_";
    @@ -99,6 +100,8 @@ public class JobStartPagePeasRequestRouter extends ComponentRequestRouter<JobSta
       private static final String ROLE_INSTANCE_FULL_DEST = "/jobStartPagePeas/jsp/roleInstance.jsp";
       private static final String ROLE_ITEMS_PREFIX = "roleItems";
       private static final String SPACE_TYPE = "Space";
    +  private static final String WRITE_OPERATION_PARTS =
    +      "(?i)^.*(create|update|modify|delete|remove|effective).*$";
     
       @Override
       public JobStartPagePeasSessionController createComponentSessionController(
    @@ -233,10 +236,12 @@ public String getDestinationNavBar(String function,
           }
           destination = "/jobStartPagePeas/jsp/welcome.jsp";
         } else if (VIEW_BIN_FCT.equals(function)) {
    +      jobStartPageSC.checkAdminAccessOnly();
           request.setAttribute("Spaces", jobStartPageSC.getRemovedSpaces());
           request.setAttribute("Components", jobStartPageSC.getRemovedComponents());
           destination = "/jobStartPagePeas/jsp/bin.jsp";
         } else if ("RestoreFromBin".equals(function) || "RemoveDefinitely".equals(function)) {
    +      jobStartPageSC.checkAdminAccessOnly();
           final List<String> spaceIds;
           final List<String> componentIds;
           final String itemId = request.getParameter("ItemId");
    @@ -304,13 +309,9 @@ public String getDestinationComponent(String function,
               COMPONENT_INFO_FULL_DEST;
         } else if ("SetupComponent".equals(function)) {
           String compoId = request.getParameter(COMPONENT_ID_PARAM);
    -      if (jobStartPageSC.isComponentManageable(compoId)) {
    -        jobStartPageSC.setManagedInstanceId(compoId,
    -            JobStartPagePeasSessionController.SCOPE_FRONTOFFICE);
    -        destination = getDestination("UpdateInstance", jobStartPageSC, request);
    -      } else {
    -        destination = "/admin/jsp/accessForbidden.jsp";
    -      }
    +      jobStartPageSC.setManagedInstanceId(compoId,
    +          JobStartPagePeasSessionController.SCOPE_FRONTOFFICE);
    +      destination = getDestination("UpdateInstance", jobStartPageSC, request);
         } else if (GO_TO_CURRENT_COMPONENT_FCT.equals(function)) {
           destination = COMPONENT_INFO_FULL_DEST;
         } else if ("ListComponent".equals(function)) {
    @@ -466,15 +467,9 @@ public String getDestinationComponent(String function,
             destination = getDestination(WELCOME_FCT, jobStartPageSC, request);
           }
         } else if (function.equals("OpenComponent")) {
    -      // check if user can update it
    -      String id = request.getParameter(COMPONENT_ID_PARAM);
    -      if (!jobStartPageSC.isComponentManageable(id)) {
    -        destination = "/admin/jsp/accessForbidden.jsp";
    -      } else {
    -        jobStartPageSC.init();
    -        request.setAttribute("PopupMode", true);
    -        destination = getDestination(GO_TO_COMPONENT_FCT, jobStartPageSC, request);
    -      }
    +      jobStartPageSC.init();
    +      request.setAttribute("PopupMode", true);
    +      destination = getDestination(GO_TO_COMPONENT_FCT, jobStartPageSC, request);
         }
     
         return destination;
    @@ -718,8 +713,13 @@ public String getDestinationSpace(String function,
        * "/almanach/jsp/almanach.jsp?flag=user")
        */
       @Override
    -  public String getDestination(String function, JobStartPagePeasSessionController jobStartPageSC,
    +  public String getAdminDestination(String function, JobStartPagePeasSessionController jobStartPageSC,
           HttpRequest request) {
    +    if (function.matches(WRITE_OPERATION_PARTS)) {
    +      final String spaceId = jobStartPageSC.getManagedSpaceId();
    +      final String instanceId = jobStartPageSC.getManagedInstanceId();
    +      jobStartPageSC.checkAccessGranted(spaceId, instanceId, false);
    +    }
         if (request.getParameterAsBoolean(HAVE_TO_REFRESH_NAV_BAR_ATTR)) {
           request.setAttribute(HAVE_TO_REFRESH_NAV_BAR_ATTR, Boolean.TRUE);
         }
    
  • core-war/src/main/java/org/silverpeas/web/pdc/control/PdcSessionController.java+13 2 modified
    @@ -23,6 +23,7 @@
      */
     package org.silverpeas.web.pdc.control;
     
    +import org.silverpeas.core.SilverpeasRuntimeException;
     import org.silverpeas.core.admin.service.AdminController;
     import org.silverpeas.core.admin.user.model.Group;
     import org.silverpeas.core.admin.user.model.UserDetail;
    @@ -40,7 +41,7 @@
     import org.silverpeas.core.util.ServiceProvider;
     import org.silverpeas.core.util.URLUtil;
     import org.silverpeas.core.util.logging.SilverLogger;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.web.selection.Selection;
    @@ -54,7 +55,8 @@
     import java.util.Iterator;
     import java.util.List;
     
    -public class PdcSessionController extends AbstractComponentSessionController {
    +public class PdcSessionController extends AbstractAdminComponentSessionController {
    +  private static final long serialVersionUID = -7993993070048344281L;
     
       private String currentView = "P";
       private Axis currentAxis = null;
    @@ -76,6 +78,15 @@ public PdcSessionController(MainSessionController mainSessionCtrl,
         currentLanguage = getLanguage();
       }
     
    +  @Override
    +  public boolean isAccessGranted() {
    +    try {
    +      return isPDCAdmin() || getPdcBm().isUserManager(getUserId());
    +    } catch (PdcException e) {
    +      throw new SilverpeasRuntimeException(e);
    +    }
    +  }
    +
       private PdcManager getPdcBm() {
         return PdcServiceProvider.getPdcManager();
       }
    
  • core-war/src/main/java/org/silverpeas/web/pdc/servlets/PdcRequestRouter.java+3 3 modified
    @@ -34,7 +34,7 @@
     import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     import org.silverpeas.core.web.mvc.util.AccessForbiddenException;
     import org.silverpeas.web.pdc.control.PdcSessionController;
     
    @@ -44,7 +44,7 @@
     import java.util.List;
     import java.util.StringTokenizer;
     
    -public class PdcRequestRouter extends ComponentRequestRouter<PdcSessionController> {
    +public class PdcRequestRouter extends AdminComponentRequestRouter<PdcSessionController> {
     
       private static final long serialVersionUID = -1233766141114104308L;
     
    @@ -85,7 +85,7 @@ public String getSessionControlBeanName() {
        * "/notificationUser/jsp/notificationUser.jsp?flag=user")
        */
       @Override
    -  public String getDestination(String function, PdcSessionController pdcSC,
    +  public String getAdminDestination(String function, PdcSessionController pdcSC,
           HttpRequest request) {
         String destination = "";
         try {
    
  • core-war/src/main/java/org/silverpeas/web/silverstatistics/control/SilverStatisticsPeasSessionController.java+8 2 modified
    @@ -58,7 +58,7 @@
     import org.silverpeas.core.util.UnitUtil;
     import org.silverpeas.core.util.logging.SilverLogger;
     import org.silverpeas.core.util.memory.MemoryUnit;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.web.selection.Selection;
    @@ -85,7 +85,8 @@
      * Class declaration
      * @author
      */
    -public class SilverStatisticsPeasSessionController extends AbstractComponentSessionController {
    +public class SilverStatisticsPeasSessionController extends AbstractAdminComponentSessionController {
    +  private static final long serialVersionUID = -8394342857531676676L;
     
       public static final int INDICE_VALUE = 0;
       public static final int INDICE_LIB = 1;
    @@ -174,6 +175,11 @@ public SilverStatisticsPeasSessionController(MainSessionController mainSessionCt
         initYears();
       }
     
    +  @Override
    +  public boolean isAccessGranted() {
    +    return isAccessGranted(null, null, true);
    +  }
    +
       public UserAccessLevel getUserProfile() {
         if (getUserDetail().isAccessAdmin()) {
           return getUserDetail().getAccessLevel();
    
  • core-war/src/main/java/org/silverpeas/web/silverstatistics/servlets/SilverStatisticsPeasRequestRouter.java+20 8 modified
    @@ -23,35 +23,39 @@
      */
     package org.silverpeas.web.silverstatistics.servlets;
     
    +import org.apache.commons.lang3.math.NumberUtils;
    +import org.silverpeas.core.admin.user.constant.UserAccessLevel;
    +import org.silverpeas.core.chart.period.PeriodChart;
    +import org.silverpeas.core.chart.pie.PieChart;
     import org.silverpeas.core.util.StringUtil;
    +import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     import org.silverpeas.web.silverstatistics.control.SilverStatisticsPeasSessionController;
     import org.silverpeas.web.silverstatistics.vo.AxisStatsFilter;
     import org.silverpeas.web.silverstatistics.vo.CrossAxisStatsFilter;
     import org.silverpeas.web.silverstatistics.vo.CrossStatisticVO;
     import org.silverpeas.web.silverstatistics.vo.StatisticVO;
    -import org.apache.commons.lang3.math.NumberUtils;
    -import org.silverpeas.core.admin.user.constant.UserAccessLevel;
    -import org.silverpeas.core.chart.period.PeriodChart;
    -import org.silverpeas.core.chart.pie.PieChart;
    -import org.silverpeas.core.web.http.HttpRequest;
     
     import javax.servlet.http.HttpServletRequest;
     import java.util.Calendar;
     import java.util.List;
     
    +import static org.silverpeas.core.util.StringUtil.isNotDefined;
    +
     /**
      * Class declaration
      * @author
      */
     public class SilverStatisticsPeasRequestRouter extends
    -    ComponentRequestRouter<SilverStatisticsPeasSessionController> {
    +    AdminComponentRequestRouter<SilverStatisticsPeasSessionController> {
     
       private static final long serialVersionUID = -7422373100761515806L;
       private final SilverStatisticsActionAccessController actionAccessController =
           new SilverStatisticsActionAccessController();
    +  private static final String PDC_FUNCTION = "(?i)^.*(pdc).*$";
    +  private static final String SPACE_MANAGER_FUNCTION = "(?i)^.*(access|volume).*$";
     
       /**
        * Method declaration
    @@ -87,7 +91,7 @@ public String getSessionControlBeanName() {
        * "/almanach/jsp/almanach.jsp?flag=user")
        */
       @Override
    -  public String getDestination(String function, SilverStatisticsPeasSessionController statsSC,
    +  public String getAdminDestination(String function, SilverStatisticsPeasSessionController statsSC,
           HttpRequest request) {
         String destination = "";
         UserAccessLevel userProfile = statsSC.getUserProfile();
    @@ -98,6 +102,14 @@ public String getDestination(String function, SilverStatisticsPeasSessionControl
           return null;
         }
     
    +    if (function.matches(SPACE_MANAGER_FUNCTION) && !function.matches(PDC_FUNCTION)) {
    +      final String accessSpaceId = StringUtil.defaultStringIfNotDefined(
    +          request.getParameter("SpaceId"), statsSC.getAccessSpaceId());
    +      statsSC.checkAccessGranted(accessSpaceId, null, isNotDefined(accessSpaceId));
    +    } else {
    +      statsSC.checkAdminAccessOnly();
    +    }
    +
         Calendar calendar = Calendar.getInstance();
         String monthOfCurrentYear = String.valueOf(calendar.get(Calendar.MONTH));
         String currentYear = String.valueOf(calendar.get(Calendar.YEAR));
    
  • core-war/src/main/java/org/silverpeas/web/templatedesigner/control/TemplateDesignerSessionController.java+3 2 modified
    @@ -45,7 +45,7 @@
     import org.silverpeas.core.util.file.FileFolderManager;
     import org.silverpeas.core.util.file.FileRepositoryManager;
     import org.silverpeas.core.util.logging.SilverLogger;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.web.mvc.webcomponent.WebMessager;
    @@ -64,7 +64,8 @@
     import java.util.Map;
     import java.util.Objects;
     
    -public class TemplateDesignerSessionController extends AbstractComponentSessionController {
    +public class TemplateDesignerSessionController extends AbstractAdminComponentSessionController {
    +  private static final long serialVersionUID = -6869148156113542542L;
     
       private static final String VIEW_HTML = "view.html";
       private static final String UPDATE_HTML = "update.html";
    
  • core-war/src/main/java/org/silverpeas/web/templatedesigner/servlets/TemplateDesignerRequestRouter.java+15 18 modified
    @@ -23,42 +23,39 @@
      */
     package org.silverpeas.web.templatedesigner.servlets;
     
    -import java.io.File;
    -import java.io.IOException;
    -import java.util.Arrays;
    -import java.util.Enumeration;
    -import java.util.List;
    -
    -import javax.servlet.http.HttpServletRequest;
    -
     import org.apache.commons.fileupload.FileItem;
    -import org.silverpeas.core.contribution.content.form.field.PublicationsPickerField;
    -import org.silverpeas.core.security.encryption.cipher.CryptoException;
    -
     import org.silverpeas.core.contribution.content.form.DataRecord;
     import org.silverpeas.core.contribution.content.form.FieldTemplate;
     import org.silverpeas.core.contribution.content.form.Form;
     import org.silverpeas.core.contribution.content.form.FormException;
     import org.silverpeas.core.contribution.content.form.PagesContext;
    -import org.silverpeas.core.pdc.form.fieldtype.PdcField;
    +import org.silverpeas.core.contribution.content.form.field.PublicationsPickerField;
     import org.silverpeas.core.contribution.content.form.record.GenericFieldTemplate;
     import org.silverpeas.core.contribution.content.form.record.Label;
     import org.silverpeas.core.contribution.content.form.record.Parameter;
     import org.silverpeas.core.contribution.content.form.record.ParameterValue;
     import org.silverpeas.core.contribution.template.publication.PublicationTemplate;
     import org.silverpeas.core.contribution.template.publication.PublicationTemplateImpl;
    -import org.silverpeas.web.templatedesigner.control.TemplateDesignerSessionController;
    +import org.silverpeas.core.pdc.form.fieldtype.PdcField;
    +import org.silverpeas.core.security.encryption.cipher.CryptoException;
     import org.silverpeas.core.util.StringUtil;
    +import org.silverpeas.core.util.file.FileRepositoryManager;
     import org.silverpeas.core.util.file.FileUploadUtil;
     import org.silverpeas.core.web.http.HttpRequest;
    -
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    -import org.silverpeas.core.util.file.FileRepositoryManager;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
    +import org.silverpeas.web.templatedesigner.control.TemplateDesignerSessionController;
    +
    +import javax.servlet.http.HttpServletRequest;
    +import java.io.File;
    +import java.io.IOException;
    +import java.util.Arrays;
    +import java.util.Enumeration;
    +import java.util.List;
     
     public class TemplateDesignerRequestRouter extends
    -    ComponentRequestRouter<TemplateDesignerSessionController> {
    +    AdminComponentRequestRouter<TemplateDesignerSessionController> {
     
       private static final long serialVersionUID = 1117593114737219878L;
     
    @@ -96,7 +93,7 @@ public TemplateDesignerSessionController createComponentSessionController(
        * "/almanach/jsp/almanach.jsp?flag=user")
        */
       @Override
    -  public String getDestination(String function,
    +  public String getAdminDestination(String function,
           TemplateDesignerSessionController templateDesignerSC,
           HttpRequest request) {
         String destination = "";
    
  • core-war/src/main/java/org/silverpeas/web/variables/VariablesRequestRouter.java+5 8 modified
    @@ -23,15 +23,16 @@
      */
     package org.silverpeas.web.variables;
     
    -import org.silverpeas.core.admin.user.model.User;
     import org.silverpeas.core.util.SilverpeasList;
     import org.silverpeas.core.util.StringUtil;
     import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     
    -public class VariablesRequestRouter extends ComponentRequestRouter<VariablesSessionController> {
    +public class VariablesRequestRouter
    +    extends AdminComponentRequestRouter<VariablesSessionController> {
    +  private static final long serialVersionUID = 880659914133424280L;
     
       private static final String SESSION_BEAN_NAME = "Variables";
     
    @@ -41,13 +42,9 @@ public String getSessionControlBeanName() {
       }
     
       @Override
    -  public String getDestination(final String action, final VariablesSessionController sc,
    +  public String getAdminDestination(final String action, final VariablesSessionController sc,
           final HttpRequest request) {
     
    -    if (!User.getCurrentRequester().isAccessAdmin()) {
    -      throwHttpForbiddenError();
    -    }
    -
         String destination = "/variables/jsp/variables.jsp";
     
         if ("Main".equals(action)) {
    
  • core-war/src/main/java/org/silverpeas/web/variables/VariablesSessionController.java+3 2 modified
    @@ -24,7 +24,7 @@
     package org.silverpeas.web.variables;
     
     import org.silverpeas.core.variables.Variable;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.webapi.variables.VariablesWebManager;
    @@ -34,7 +34,8 @@
     import java.util.List;
     import java.util.Set;
     
    -public class VariablesSessionController extends AbstractComponentSessionController {
    +public class VariablesSessionController extends AbstractAdminComponentSessionController {
    +  private static final long serialVersionUID = -6077929073402709139L;
     
       private final Set<String> selectedValueIds = new HashSet<>();
       private Variable currentVariable;
    
  • core-war/src/main/java/org/silverpeas/web/workflowdesigner/control/WorkflowDesignerSessionController.java+2 2 modified
    @@ -37,7 +37,7 @@
     import org.silverpeas.core.util.StringUtil;
     import org.silverpeas.core.util.file.FileServerUtils;
     import org.silverpeas.core.util.file.FileUtil;
    -import org.silverpeas.core.web.mvc.controller.AbstractComponentSessionController;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
     import org.silverpeas.core.workflow.api.ProcessModelManager;
    @@ -59,7 +59,7 @@
     import static java.util.Optional.ofNullable;
     import static org.silverpeas.core.util.file.FileRepositoryManager.getTemporaryPath;
     
    -public class WorkflowDesignerSessionController extends AbstractComponentSessionController {
    +public class WorkflowDesignerSessionController extends AbstractAdminComponentSessionController {
     
       public static final String TYPE_USER = "user";
       public static final String FORMS = "forms";
    
  • core-war/src/main/java/org/silverpeas/web/workflowdesigner/servlets/WorkflowDesignerRequestRouter.java+4 9 modified
    @@ -25,12 +25,11 @@
     
     import org.apache.commons.fileupload.FileItem;
     import org.silverpeas.core.exception.SilverpeasException;
    -import org.silverpeas.core.util.ResourceLocator;
     import org.silverpeas.core.util.StringUtil;
     import org.silverpeas.core.web.http.HttpRequest;
     import org.silverpeas.core.web.mvc.controller.ComponentContext;
     import org.silverpeas.core.web.mvc.controller.MainSessionController;
    -import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
     import org.silverpeas.core.workflow.api.Workflow;
     import org.silverpeas.core.workflow.api.WorkflowException;
     import org.silverpeas.core.workflow.api.model.*;
    @@ -51,7 +50,7 @@
     import java.util.StringTokenizer;
     
     public class WorkflowDesignerRequestRouter extends
    -    ComponentRequestRouter<WorkflowDesignerSessionController> {
    +    AdminComponentRequestRouter<WorkflowDesignerSessionController> {
     
       private static final long serialVersionUID = -6747786008527861783L;
       static private Map<String, FunctionHandler> mapHandler; // mapping of functions to their handlers
    @@ -90,16 +89,12 @@ public WorkflowDesignerSessionController createComponentSessionController(
        * @return The complete destination URL for a forward (ex :
        * "/almanach/jsp/almanach.jsp?flag=user")
        */
    -  public String getDestination(String function,
    +  @Override
    +  public String getAdminDestination(String function,
           WorkflowDesignerSessionController workflowDesignerSC, HttpRequest request) {
         String destination = null;
         FunctionHandler handler = getHandler(function);
     
    -    // Check access rights
    -    if (!workflowDesignerSC.getUserDetail().isAccessAdmin()) {
    -      return ResourceLocator.getGeneralSettingBundle().getString("accessForbidden");
    -    }
    -
         try {
           if (handler != null) {
             destination = handler.getDestination(function, workflowDesignerSC, request);
    
  • core-war/src/main/webapp/admin/jsp/cipherkey.jsp+7 1 modified
    @@ -1,4 +1,4 @@
    -<%--
    +<%@ page import="org.silverpeas.core.admin.user.model.User" %><%--
     
         Copyright (C) 2000 - 2022 Silverpeas
     
    @@ -26,9 +26,15 @@
     <%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
    +<%@ taglib uri="http://www.silverpeas.com/tld/silverFunctions" prefix="silfn" %>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
     <%@ taglib uri="http://www.silverpeas.com/tld/viewGenerator" prefix="view"%>
     
    +<c:set var="currentUser" value="${silfn:currentUser()}"/>
    +<c:if test="${currentUser == null or not currentUser.accessAdmin}">
    +  <c:redirect url="/Login"/>
    +</c:if>
    +
     <fmt:setLocale value="${sessionScope['SilverSessionController'].favoriteLanguage}" />
     <view:setBundle basename="org.silverpeas.crypto.multilang.cryptoBundle" />
     
    
  • core-war/src/main/webapp/admin/jsp/errorpageMain.jsp+17 0 modified
    @@ -46,6 +46,10 @@ if (response.isCommitted() == false) {
     <%@ page import="org.silverpeas.core.util.WebEncodeHelper" %>
     <%@ page import="org.silverpeas.core.web.mvc.util.HomePageUtil" %>
     <%@ page import="java.io.PrintWriter" %>
    +<%@ page import="org.silverpeas.core.security.authorization.ForbiddenRuntimeException" %>
    +<%@ page import="javax.ws.rs.WebApplicationException" %>
    +<%@ page import="org.silverpeas.core.util.logging.SilverLogger" %>
    +<%@ page import="org.silverpeas.core.web.mvc.webcomponent.SilverpeasHttpServlet" %>
     
     <%@ include file="import.jsp" %>
     
    @@ -64,6 +68,19 @@ if (exception instanceof SilverpeasTrappedException) {
       extraInfos = StringUtil.defaultStringIfNotDefined(ste.getExtraInfos(), null);
       // Trace the exception
       HomePageUtil.traceException(exception);
    +} else if (exception instanceof ForbiddenRuntimeException) {
    +  SilverLogger.getLogger(SilverpeasHttpServlet.class).error(exception.getMessage());
    +  response.sendError(HttpServletResponse.SC_FORBIDDEN, exception.getMessage());
    +  return;
    +} else if (exception instanceof WebApplicationException) {
    +  final WebApplicationException wae = (WebApplicationException) exception;
    +  if (wae.getResponse().getStatus() == HttpServletResponse.SC_FORBIDDEN) {
    +    SilverLogger.getLogger(SilverpeasHttpServlet.class).error(wae.getMessage());
    +  }
    +  if (!response.isCommitted()) {
    +    response.sendError(wae.getResponse().getStatus(), exception.getMessage());
    +  }
    +  return;
     } else {
       // Trace the exception
       HomePageUtil.traceException(toDisplayException);
    
  • core-war/src/main/webapp/admin/jsp/SilverLoggerAdmin.jsp+7 5 modified
    @@ -58,17 +58,19 @@
     <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
     <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
    +<%@ taglib uri="http://www.silverpeas.com/tld/silverFunctions" prefix="silfn" %>
     <%@ taglib uri="http://www.silverpeas.com/tld/viewGenerator" prefix="view" %>
    +
    +<c:set var="currentUser" value="${silfn:currentUser()}"/>
    +<c:if test="${currentUser == null or not currentUser.accessAdmin}">
    +  <c:redirect url="/Login"/>
    +</c:if>
    +
     <%
       response.setHeader("Cache-Control", "no-store"); //HTTP 1.1
       response.setHeader("Pragma", "no-cache"); //HTTP 1.0
       response.setDateHeader("Expires", -1); //prevents caching at the proxy server
     
    -  UserDetail currentUser = UserDetail.getCurrentRequester();
    -  if (currentUser == null || !currentUser.isAccessAdmin()) {
    -    request.getRequestDispatcher("../../Login.jsp").forward(request, response);
    -  }
    -
       List<String> loggingLevels = new ArrayList<>();
       for (Level level : Level.values()) {
         loggingLevels.add(level.name());
    
  • core-war/src/main/webapp/applicationIndexer/jsp/applicationIndexer.jsp+6 7 modified
    @@ -36,7 +36,6 @@ response.setDateHeader ("Expires",-1); //prevents caching at the proxy server
     %>
     
     <%@ page import="org.silverpeas.core.admin.space.SpaceInstLight"%>
    -<%@ page import="org.silverpeas.core.admin.user.constant.UserAccessLevel"%>
     
     <%@ page import="org.silverpeas.core.util.LocalizationBundle"%>
     <%@ page import="org.silverpeas.core.util.ResourceLocator"%>
    @@ -49,6 +48,7 @@ response.setDateHeader ("Expires",-1); //prevents caching at the proxy server
     
     <%@ page errorPage="../../admin/jsp/errorpage.jsp"%>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    +<%@ taglib uri="http://www.silverpeas.com/tld/silverFunctions" prefix="silfn" %>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
     <%@ taglib uri="http://www.silverpeas.com/tld/viewGenerator" prefix="view"%>
     <%@ taglib prefix="fmy" uri="http://java.sun.com/jsp/jstl/fmt" %>
    @@ -92,14 +92,13 @@ response.setDateHeader ("Expires",-1); //prevents caching at the proxy server
       }
     %>
     
    +<c:set var="currentUser" value="${silfn:currentUser()}"/>
    +<c:if test="${currentUser == null or not currentUser.accessAdmin}">
    +  <c:redirect url="/Login"/>
    +</c:if>
    +
     <%
     final MainSessionController mainSessionCtrl = (MainSessionController) session.getAttribute(MainSessionController.MAIN_SESSION_CONTROLLER_ATT);
    -if (mainSessionCtrl == null || !UserAccessLevel.ADMINISTRATOR.equals(mainSessionCtrl.getUserAccessLevel())) {
    -    // No session controller in the request -> security exception
    -    String sessionTimeout = ResourceLocator.getGeneralSettingBundle().getString("sessionTimeout");
    -    getServletConfig().getServletContext().getRequestDispatcher(sessionTimeout).forward(request, response);
    -    return;
    -}
     %>
     
     <c:set var="_userLanguage" value="<%=mainSessionCtrl.getFavoriteLanguage()%>" scope="request"/>
    
  • core-war/src/main/webapp/silverpeasinfos.jsp+2 2 modified
    @@ -55,8 +55,8 @@
     <%@ taglib uri="http://www.silverpeas.com/tld/viewGenerator" prefix="view" %>
     
     <c:set var="currentUser" value="${silfn:currentUser()}"/>
    -<c:if test="${currentUser == null or !currentUser.accessAdmin}">
    -  <c:redirect url="/Login.jsp"/>
    +<c:if test="${currentUser == null or not currentUser.accessAdmin}">
    +  <c:redirect url="/Login"/>
     </c:if>
     
     <html>
    
  • core-war/src/test/java/org/silverpeas/web/jobdomain/control/JobDomainPeasSessionControllerAccessGrantedTest.java+508 0 added
    @@ -0,0 +1,508 @@
    +/*
    + * Copyright (C) 2000 - 2023 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.web.jobdomain.control;
    +
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.DisplayName;
    +import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.api.function.Executable;
    +import org.mockito.stubbing.Answer;
    +import org.silverpeas.core.admin.domain.model.Domain;
    +import org.silverpeas.core.admin.service.OrganizationController;
    +import org.silverpeas.core.admin.user.model.Group;
    +import org.silverpeas.core.admin.user.model.UserDetail;
    +import org.silverpeas.core.admin.user.service.UserProvider;
    +import org.silverpeas.core.security.authorization.ForbiddenRuntimeException;
    +import org.silverpeas.core.test.extention.EnableSilverTestEnv;
    +import org.silverpeas.core.test.extention.TestManagedMock;
    +import org.silverpeas.core.test.extention.TestedBean;
    +import org.silverpeas.core.util.StringUtil;
    +import org.silverpeas.core.web.mvc.controller.ComponentContext;
    +
    +import java.util.List;
    +
    +import static java.util.stream.Collectors.toList;
    +import static org.junit.jupiter.api.Assertions.assertThrows;
    +import static org.mockito.ArgumentMatchers.anyString;
    +import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.when;
    +import static org.silverpeas.core.admin.domain.model.Domain.MIXED_DOMAIN_ID;
    +import static org.silverpeas.core.admin.user.constant.UserAccessLevel.USER;
    +
    +/**
    + * @author silveryocha
    + */
    +@EnableSilverTestEnv
    +class JobDomainPeasSessionControllerAccessGrantedTest {
    +
    +  private static final String LOGGED_USER_DOMAIN_ID = "26";
    +  private static final String OTHER_DOMAIN_ID = "38";
    +  private static final String USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID = "7";
    +  private static final String USER_ID_ON_OTHER_DOMAIN = "8";
    +  private static final String USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE = "9";
    +  private static final String GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID = LOGGED_USER_DOMAIN_ID;
    +  private static final String GROUP_ID_ON_OTHER_DOMAIN = OTHER_DOMAIN_ID;
    +  private static final String GROUP_ID_ON_MIXED_DOMAIN = USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID;
    +
    +  @TestManagedMock
    +  private Domain rightDomain;
    +
    +  @TestManagedMock
    +  private Domain wrongDomain;
    +
    +  @TestedBean
    +  private JobDomainPeasSessionController4Test controller;
    +
    +  @BeforeEach
    +  void setup(@TestManagedMock OrganizationController orgaController,
    +      @TestManagedMock UserProvider userProvider) {
    +    when(rightDomain.getId()).thenReturn(LOGGED_USER_DOMAIN_ID);
    +    when(wrongDomain.getId()).thenReturn(OTHER_DOMAIN_ID);
    +    controller.setupDefaultLoggedUser();
    +    final Answer<Object> getUserAnswer = i -> {
    +      final String id = i.getArgument(0);
    +      final UserDetail user = mock(UserDetail.class);
    +      when(user.getId()).thenReturn(id);
    +      when(user.getDomainId()).thenAnswer(a -> {
    +        switch (id) {
    +          case USER_ID_ON_OTHER_DOMAIN:
    +            return OTHER_DOMAIN_ID;
    +          case USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE:
    +            return MIXED_DOMAIN_ID;
    +          default:
    +            return LOGGED_USER_DOMAIN_ID;
    +        }
    +      });
    +      return user;
    +    };
    +    when(userProvider.getUser(anyString())).thenAnswer(getUserAnswer);
    +    when(orgaController.getUserDetail(anyString())).thenAnswer(getUserAnswer);
    +    when(orgaController.getGroup(anyString())).thenAnswer(i -> {
    +      final String id = i.getArgument(0);
    +      final int index = id.indexOf("_");
    +      final String topLevelGroupId = index > 0 ? id.substring(0, index) : id;
    +      final Group group = mock(Group.class);
    +      when(group.getId()).thenReturn(id);
    +      when(group.getDomainId()).thenAnswer(a -> {
    +        switch (topLevelGroupId) {
    +          case GROUP_ID_ON_OTHER_DOMAIN:
    +            return OTHER_DOMAIN_ID;
    +          case GROUP_ID_ON_MIXED_DOMAIN:
    +            return MIXED_DOMAIN_ID;
    +          default:
    +            return LOGGED_USER_DOMAIN_ID;
    +        }
    +      });
    +      if (index > 0) {
    +        when(group.getSuperGroupId()).thenReturn(topLevelGroupId);
    +      }
    +      return group;
    +    });
    +    when(orgaController.getPathToGroup(anyString())).thenAnswer(i -> {
    +      final String id = i.getArgument(0);
    +      final int index = id.indexOf("_");
    +      if (index > 0) {
    +        return List.of(id.substring(0, index));
    +      } else {
    +        return List.of();
    +      }
    +    });
    +  }
    +
    +  /**
    +   * DOMAIN
    +   */
    +
    +  @DisplayName("User with admin access level has always granted access to a domain")
    +  @Test
    +  void adminDomainAccessGranted() {
    +    when(controller.getUserDetail().isAccessAdmin()).thenReturn(true);
    +    controller.checkDomainAccessGranted(MIXED_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false);
    +    controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false);
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false);
    +  }
    +
    +  @DisplayName("User with user access level has never granted access to a domain")
    +  @Test
    +  void simpleUserDomainAccessNotGranted() {
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +  }
    +
    +  @DisplayName("User with domain access level, without managed group, has granted access if " +
    +      "accessed domain corresponds to its domain")
    +  @Test
    +  void domainAdminUserDomainAccess() {
    +    when(controller.getUserDetail().isAccessDomainManager()).thenReturn(true);
    +    assertDomainAdminUserDomainAccess();
    +    when(controller.getUserDetail().isDomainAdminRestricted()).thenReturn(true);
    +    assertDomainAdminUserDomainAccess();
    +  }
    +
    +  private void assertDomainAdminUserDomainAccess() {
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +  }
    +
    +  @DisplayName("User with domain access level, and with managed groups, has granted access if " +
    +      "accessed domain does not correspond to its domain")
    +  @Test
    +  void domainAdminUserDomainAccessWithManageableGroups() {
    +    when(controller.getUserDetail().isAccessDomainManager()).thenReturn(true);
    +    when(controller.getUserDetail().getDomainId()).thenReturn(OTHER_DOMAIN_ID);
    +    assertDomainAdminUserDomainAccessWithManageableGroups(false);
    +    when(controller.getUserDetail().isDomainAdminRestricted()).thenReturn(true);
    +    assertDomainAdminUserDomainAccessWithManageableGroups(true);
    +  }
    +
    +  private void assertDomainAdminUserDomainAccessWithManageableGroups(boolean domainRestricted) {
    +    controller.setManageableGroupIds();
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false);
    +    // setting now the managed groups on mixed domain
    +    controller.setManageableGroupIds(MIXED_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(MIXED_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false);
    +    // setting now the managed groups on same domain of LOGGED user domain
    +    controller.setManageableGroupIds(LOGGED_USER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    if (domainRestricted) {
    +      assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    } else {
    +      controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID);
    +    }
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false);
    +    // setting now the managed groups on other domain than the one of LOGGED user domain
    +    controller.setManageableGroupIds(OTHER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false);
    +  }
    +
    +  @DisplayName("User with user access level, and with managed groups, has granted access if " +
    +      "accessed domain does not correspond to its domain")
    +  @Test
    +  void simpleUserDomainAccessWithManageableGroups() {
    +    when(controller.getUserDetail().getDomainId()).thenReturn(OTHER_DOMAIN_ID);
    +    assertSimpleUserDomainAccessWithManageableGroups(false);
    +    when(controller.getUserDetail().isDomainAdminRestricted()).thenReturn(true);
    +    assertSimpleUserDomainAccessWithManageableGroups(true);
    +  }
    +
    +  private void assertSimpleUserDomainAccessWithManageableGroups(boolean domainRestricted) {
    +    controller.setManageableGroupIds();
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +    // setting now the managed groups on mixed domain
    +    controller.setManageableGroupIds(MIXED_DOMAIN_ID);
    +    controller.checkDomainAccessGranted(MIXED_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +    // setting now the managed groups on same domain of LOGGED user domain
    +    controller.setManageableGroupIds(LOGGED_USER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    if (domainRestricted) {
    +      assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    } else {
    +      controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID);
    +    }
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +    // setting now the managed groups on other domain than the one of LOGGED user domain
    +    controller.setManageableGroupIds(OTHER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(OTHER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +  }
    +
    +  @DisplayName("User with user access level, without managed groups and with space management " +
    +      "right, has granted access in read only mode")
    +  @Test
    +  void simpleUserDomainAccessWhenSpaceManager() {
    +    controller.setManageableSpaceIds();
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +    controller.setManageableSpaceIds("3");
    +    controller.checkDomainAccessGranted(MIXED_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +  }
    +
    +  @DisplayName("User with user access level, without managed groups and community manager " +
    +      "right, has granted access in read only mode")
    +  @Test
    +  void simpleUserDomainAccessWhenCommunityManager() {
    +    controller.setCommunityManager(true);
    +    controller.checkDomainAccessGranted(MIXED_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(MIXED_DOMAIN_ID, false));
    +    controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkDomainAccessGranted(LOGGED_USER_DOMAIN_ID, false));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID));
    +    assertForbidden(() -> controller.checkDomainAccessGranted(OTHER_DOMAIN_ID, false));
    +  }
    +
    +  /**
    +   * USER
    +   */
    +
    +  @DisplayName("When admin modifying a user, domain access in write mode is verified and " +
    +      "target domain MUST be set to the right domain")
    +  @Test
    +  void adminUserAccessGranted() {
    +    controller.setManageableGroupIds(GROUP_ID_ON_MIXED_DOMAIN, GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID,
    +        GROUP_ID_ON_OTHER_DOMAIN);
    +    assertAdminUserAccessGranted();
    +    controller.setManageableSpaceIds("3");
    +    assertAdminUserAccessGranted();
    +  }
    +
    +  private void assertAdminUserAccessGranted() {
    +    controller.setTargetDomain(null);
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, false));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, false));
    +    // target domain is the mixed one
    +    controller.setTargetDomain(MIXED_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, false));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, false));
    +    // target domain is the one of the admin
    +    controller.setTargetDomain(LOGGED_USER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, false));
    +    controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true);
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, false));
    +    // target domain is not the one of the admin
    +    controller.setTargetDomain(OTHER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_MIXED_DOMAIN_EVEN_IF_NOT_POSSIBLE, false));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true));
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, true);
    +    assertForbidden(() -> controller.checkUserAccessGranted(USER_ID_ON_OTHER_DOMAIN, false));
    +  }
    +
    +  /**
    +   * GROUP
    +   */
    +
    +  @DisplayName("When admin modifying a group, domain access in write mode is verified and " +
    +      "target domain MUST be set to the right domain")
    +  @Test
    +  void adminGroupAccessGranted() {
    +    controller.setManageableGroupIds(GROUP_ID_ON_MIXED_DOMAIN, GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID,
    +        GROUP_ID_ON_OTHER_DOMAIN);
    +    assertAdminGroupAccessGranted();
    +    controller.setManageableSpaceIds("4");
    +    assertAdminUserAccessGranted();
    +  }
    +
    +  private void assertAdminGroupAccessGranted() {
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, false));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, false));
    +    // target domain is the mixed one
    +    controller.setTargetDomain(MIXED_DOMAIN_ID);
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, true);
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, false);
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, false));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN + "_sub", true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN + "_sub", false));
    +    // target domain is the one of the admin
    +    controller.setTargetDomain(LOGGED_USER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, false));
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true);
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false);
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, false));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN + "_sub", true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN + "_sub", false));
    +    // target domain is not the one of the admin
    +    controller.setTargetDomain(OTHER_DOMAIN_ID);
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_MIXED_DOMAIN, false));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, true));
    +    assertForbidden(() -> controller.checkGroupAccessGranted(GROUP_ID_ON_DOMAIN_OF_LOGGED_USER_ID, false));
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, true);
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN, false);
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN + "_sub", true);
    +    controller.checkGroupAccessGranted(GROUP_ID_ON_OTHER_DOMAIN + "_sub", false);
    +  }
    +
    +  private void assertForbidden(final Executable executable) {
    +    assertThrows(ForbiddenRuntimeException.class, executable);
    +  }
    +
    +  private static class JobDomainPeasSessionController4Test extends JobDomainPeasSessionController {
    +    private static final long serialVersionUID = -2464677549792058075L;
    +
    +    private final UserDetail loggedUser = mock(UserDetail.class);
    +    private boolean communityManager = false;
    +    private String[] manageableSpaceIds = new String[]{};
    +    private List<String> manageableGroupIds = List.of();
    +    private Domain targetDomain = null;
    +
    +    public JobDomainPeasSessionController4Test() {
    +      super(null, mockedContext(), null, null, null);
    +    }
    +
    +    private static ComponentContext mockedContext() {
    +      final ComponentContext mock = mock(ComponentContext.class);
    +      when(mock.getCurrentComponentName()).thenReturn("unknown");
    +      return mock;
    +    }
    +
    +    private void setupDefaultLoggedUser() {
    +      when(loggedUser.getId()).thenReturn("2");
    +      when(loggedUser.getDomainId()).thenReturn(LOGGED_USER_DOMAIN_ID);
    +      when(loggedUser.getAccessLevel()).thenReturn(USER);
    +    }
    +
    +    public void setCommunityManager(final boolean communityManager) {
    +      this.communityManager = communityManager;
    +    }
    +
    +    public void setManageableSpaceIds(String... spaceIds) {
    +      manageableSpaceIds = spaceIds;
    +    }
    +
    +    /**
    +     * Sets the manageable group identifiers.
    +     * <p>
    +     * IMPORTANT: the given identifier is used to set domain id too. So giving the identifier
    +     * of domain.
    +     * </p>
    +     * @param groupIds several identifiers.
    +     */
    +    public void setManageableGroupIds(String... groupIds) {
    +      manageableGroupIds = List.of(groupIds);
    +    }
    +
    +    @Override
    +    public void setTargetDomain(final String targetDomainId) {
    +      if (StringUtil.isDefined(targetDomainId)) {
    +        this.targetDomain = mock(Domain.class);
    +        when(this.targetDomain.getId()).thenReturn(targetDomainId);
    +      } else {
    +        this.targetDomain = null;
    +      }
    +    }
    +
    +    @Override
    +    public Domain getTargetDomain() {
    +      return targetDomain;
    +    }
    +
    +    @Override
    +    public boolean isCommunityManager() {
    +      return communityManager;
    +    }
    +
    +    @Override
    +    public UserDetail getUserDetail() {
    +      return loggedUser;
    +    }
    +
    +    @Override
    +    protected String[] getUserManageableSpaceIds() {
    +      return manageableSpaceIds;
    +    }
    +
    +    @Override
    +    protected List<String> getUserManageableGroupIds() {
    +      return manageableGroupIds;
    +    }
    +
    +    @Override
    +    public List<Group> getUserManageableGroups() {
    +      return manageableGroupIds.stream().map(i -> {
    +        Group group = mock(Group.class);
    +        when(group.getId()).thenReturn(i);
    +        when(group.getDomainId()).thenReturn(
    +            GROUP_ID_ON_MIXED_DOMAIN.equals(i) ? null : i);
    +        return group;
    +      }).collect(toList());
    +    }
    +  }
    +}
    \ No newline at end of file
    
  • core-war/src/test/java/org/silverpeas/web/jobstartpage/control/JobStartPagePeasSessionControllerTest.java+301 0 added
    @@ -0,0 +1,301 @@
    +/*
    + * Copyright (C) 2000 - 2023 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.web.jobstartpage.control;
    +
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.DisplayName;
    +import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.api.function.Executable;
    +import org.silverpeas.core.admin.component.model.SilverpeasComponentInstance;
    +import org.silverpeas.core.admin.service.OrganizationController;
    +import org.silverpeas.core.admin.space.SpaceInst;
    +import org.silverpeas.core.admin.space.SpaceInstLight;
    +import org.silverpeas.core.admin.user.model.UserDetail;
    +import org.silverpeas.core.security.authorization.ForbiddenRuntimeException;
    +import org.silverpeas.core.test.extention.EnableSilverTestEnv;
    +import org.silverpeas.core.test.extention.TestManagedMock;
    +import org.silverpeas.core.test.extention.TestedBean;
    +import org.silverpeas.core.web.mvc.controller.ComponentContext;
    +
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.stream.Collectors;
    +import java.util.stream.Stream;
    +
    +import static java.lang.Integer.parseInt;
    +import static java.util.Optional.of;
    +import static java.util.Optional.ofNullable;
    +import static java.util.function.Predicate.not;
    +import static org.junit.jupiter.api.Assertions.assertThrows;
    +import static org.mockito.ArgumentMatchers.anyString;
    +import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.when;
    +import static org.silverpeas.core.admin.space.SpaceInst.SPACE_KEY_PREFIX;
    +import static org.silverpeas.core.admin.user.constant.UserAccessLevel.USER;
    +
    +/**
    + * @author silveryocha
    + */
    +@EnableSilverTestEnv
    +class JobStartPagePeasSessionControllerTest {
    +
    +  private static final String LOGGED_USER_DOMAIN_ID = "26";
    +
    +  private static final String SPACE_A = "WA1";
    +  private static final String SPACE_A_A1 = "WA11";
    +  private static final String SPACE_A_A1_A11 = "WA111";
    +  private static final String SPACE_A_A2 = "WA12";
    +  private static final String SPACE_B = "WA2";
    +  private static final List<String> ALL_SPACE_IDS = List.of(
    +      SPACE_A, SPACE_A_A1, SPACE_A_A1_A11, SPACE_A_A2,
    +      SPACE_B);
    +  private static final Map<String, String> SPACE_SPACE = Map.of(
    +      SPACE_A_A1, SPACE_A,
    +      SPACE_A_A1_A11, SPACE_A_A1,
    +      SPACE_A_A2, SPACE_A);
    +
    +  private static final String INSTANCE_AA = "kmelia1";
    +  private static final String INSTANCE_A_A1A = "kmelia11";
    +  private static final String INSTANCE_A_A1B = "kmelia12";
    +  private static final String INSTANCE_A_A1_A11A = "kmelia111";
    +  private static final String INSTANCE_BA = "kmelia2";
    +  private static final List<String> ALL_INSTANCE_IDS = List.of(
    +      INSTANCE_AA, INSTANCE_A_A1A, INSTANCE_A_A1B, INSTANCE_A_A1_A11A,
    +      INSTANCE_BA);
    +  private static final Map<String, String> INSTANCE_SPACE = Map.of(
    +      INSTANCE_AA, SPACE_A,
    +      INSTANCE_A_A1A, SPACE_A_A1,
    +      INSTANCE_A_A1B, SPACE_A_A1,
    +      INSTANCE_A_A1_A11A, SPACE_A_A1_A11,
    +      INSTANCE_BA, SPACE_B);
    +
    +  @TestedBean
    +  private JobStartPagePeasSessionController4Test controller;
    +
    +  @BeforeEach
    +  void setup(@TestManagedMock OrganizationController orgaController) {
    +    when(orgaController.getSpaceInstById(anyString())).thenAnswer(i -> {
    +      final String id = i.getArgument(0);
    +      final SpaceInst space = mock(SpaceInst.class);
    +      when(space.getId()).thenReturn(id);
    +      when(space.getLocalId()).thenReturn(parseInt(id.substring(SPACE_KEY_PREFIX.length())));
    +      ofNullable(SPACE_SPACE.get(id)).ifPresentOrElse(
    +          p -> when(space.getDomainFatherId()).thenReturn(p),
    +          () -> when(space.isRoot()).thenReturn(true));
    +      return space;
    +    });
    +    when(orgaController.getSpaceInstLightById(anyString())).thenAnswer(i -> {
    +      final String id = i.getArgument(0);
    +      final SpaceInstLight space = mock(SpaceInstLight.class);
    +      when(space.getId()).thenReturn(id);
    +      when(space.getLocalId()).thenReturn(parseInt(id.substring(SPACE_KEY_PREFIX.length())));
    +      ofNullable(SPACE_SPACE.get(id)).ifPresentOrElse(
    +          p -> when(space.getFatherId()).thenReturn(p),
    +          () -> when(space.isRoot()).thenReturn(true));
    +      return space;
    +    });
    +    when(orgaController.getComponentInstance(anyString())).thenAnswer(i -> {
    +      final String id = i.getArgument(0);
    +      final SilverpeasComponentInstance instance = mock(SilverpeasComponentInstance.class);
    +      when(instance.getId()).thenReturn(id);
    +      ofNullable(INSTANCE_SPACE.get(id)).ifPresent(p -> when(instance.getSpaceId()).thenReturn(p));
    +      return of(instance);
    +    });
    +    when(orgaController.getPathToSpace(anyString())).thenAnswer(i -> {
    +      final String id = i.getArgument(0, String.class);
    +      List<SpaceInstLight> path = new ArrayList<>();
    +      SpaceInstLight spaceInst = orgaController.getSpaceInstLightById(id);
    +      if (spaceInst != null) {
    +        if (!spaceInst.isRoot()) {
    +          path.addAll(orgaController.getPathToSpace(spaceInst.getFatherId()));
    +        }
    +        path.add(0, spaceInst);
    +      }
    +      return path;
    +    });
    +  }
    +
    +  @DisplayName("User with admin access level has always granted access to spaces and component " +
    +      "instances, even if space and instance ids does not exist")
    +  @Test
    +  void adminOfSpaceAndInstanceAccessGranted() {
    +    when(controller.getUserDetail().isAccessAdmin()).thenReturn(true);
    +    Stream.concat(Stream.of((String) null), ALL_SPACE_IDS.stream())
    +        .forEach(s -> Stream.concat(Stream.of((String) null), ALL_INSTANCE_IDS.stream())
    +            .forEach(i -> {
    +              controller.checkAccessGranted(s, i, true);
    +              controller.checkAccessGranted(s, i, false);
    +            }));
    +  }
    +
    +  @DisplayName(
    +      "User with user access level has never granted access to spaces and component instances")
    +  @Test
    +  void simpleUserOfSpaceAndInstanceAccessNotGranted() {
    +    Stream.concat(Stream.of((String) null), ALL_SPACE_IDS.stream())
    +        .forEach(s -> Stream.concat(Stream.of((String) null), ALL_INSTANCE_IDS.stream())
    +            .forEach(i -> {
    +              assertForbidden(() -> controller.checkAccessGranted(s, i, true));
    +              assertForbidden(() -> controller.checkAccessGranted(s, i, false));
    +            }));
    +  }
    +
    +  @DisplayName("Manager has granted access to aimed spaces")
    +  @Test
    +  void managerOfSpaceAccessGranted() {
    +    // ON SPACE A
    +    controller.setManageableSpaceIds(SPACE_A);
    +    controller.checkAccessGranted(null, null, true);
    +    assertForbidden(() -> controller.checkAccessGranted(null, null, false));
    +    controller.checkAccessGranted(SPACE_A, null, true);
    +    controller.checkAccessGranted(SPACE_A, null, false);
    +    controller.checkAccessGranted(SPACE_A_A1, null, true);
    +    controller.checkAccessGranted(SPACE_A_A1, null, false);
    +    controller.checkAccessGranted(SPACE_A_A1_A11, null, true);
    +    controller.checkAccessGranted(SPACE_A_A1_A11, null, false);
    +    controller.checkAccessGranted(SPACE_A_A2, null, true);
    +    controller.checkAccessGranted(SPACE_A_A2, null, false);
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_B, null, true));
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_B, null, false));
    +    // ON SPACE A_A1
    +    controller.setManageableSpaceIds(SPACE_A_A1);
    +    controller.checkAccessGranted(null, null, true);
    +    assertForbidden(() -> controller.checkAccessGranted(null, null, false));
    +    controller.checkAccessGranted(SPACE_A, null, true);
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A, null, false));
    +    controller.checkAccessGranted(SPACE_A_A1, null, true);
    +    controller.checkAccessGranted(SPACE_A_A1, null, false);
    +    controller.checkAccessGranted(SPACE_A_A1_A11, null, true);
    +    controller.checkAccessGranted(SPACE_A_A1_A11, null, false);
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A_A2, null, true));
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A_A2, null, false));
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_B, null, true));
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_B, null, false));
    +  }
    +
    +  @DisplayName("Manager has granted access to aimed components")
    +  @Test
    +  void managerOfSpaceComponentAccessGranted() {
    +    // ON SPACE A
    +    controller.setManageableSpaceIds(SPACE_A);
    +    controller.checkAccessGranted(null, null, true);
    +    assertForbidden(() -> controller.checkAccessGranted(null, null, false));
    +    controller.checkAccessGranted(null, INSTANCE_AA, true);
    +    controller.checkAccessGranted(null, INSTANCE_AA, false);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1A, true);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1A, false);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1_A11A, true);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1_A11A, false);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1B, true);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1B, false);
    +    assertForbidden(() -> controller.checkAccessGranted(null, INSTANCE_BA, true));
    +    assertForbidden(() -> controller.checkAccessGranted(null, INSTANCE_BA, false));
    +    // ON SPACE A_A1
    +    controller.setManageableSpaceIds(SPACE_A_A1);
    +    controller.checkAccessGranted(null, null, true);
    +    assertForbidden(() -> controller.checkAccessGranted(null, null, false));
    +    assertForbidden(() -> controller.checkAccessGranted(null, INSTANCE_AA, true));
    +    assertForbidden(() -> controller.checkAccessGranted(null, INSTANCE_AA, false));
    +    controller.checkAccessGranted(null, INSTANCE_A_A1A, true);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1A, false);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1_A11A, true);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1_A11A, false);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1B, true);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1B, false);
    +    assertForbidden(() -> controller.checkAccessGranted(null, INSTANCE_BA, true));
    +    assertForbidden(() -> controller.checkAccessGranted(null, INSTANCE_BA, false));
    +  }
    +
    +  @DisplayName("Manager has granted access if space and instance ids can be accessed")
    +  @Test
    +  void managerOfSpaceAndComponentAccessGranted() {
    +    // ON SPACE A
    +    controller.setManageableSpaceIds(SPACE_A);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1A, false);
    +    controller.checkAccessGranted(SPACE_A, INSTANCE_A_A1_A11A, false);
    +    controller.checkAccessGranted(SPACE_A, INSTANCE_A_A1B, false);
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A, INSTANCE_BA, false));
    +    // ON SPACE A_A1
    +    controller.setManageableSpaceIds(SPACE_A_A1);
    +    controller.checkAccessGranted(null, INSTANCE_A_A1A, false);
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A, INSTANCE_A_A1_A11A, false));
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A, INSTANCE_A_A1B, false));
    +    assertForbidden(() -> controller.checkAccessGranted(SPACE_A, INSTANCE_BA, false));
    +  }
    +
    +  private void assertForbidden(final Executable executable) {
    +    assertThrows(ForbiddenRuntimeException.class, executable);
    +  }
    +
    +  private static class JobStartPagePeasSessionController4Test
    +      extends JobStartPagePeasSessionController {
    +    private static final long serialVersionUID = 101781645406476047L;
    +
    +    private final UserDetail loggedUser = mock(UserDetail.class);
    +    private String[] manageableSpaceIds = new String[]{};
    +
    +    public JobStartPagePeasSessionController4Test() {
    +      super(null, mockedContext(), null, null, null);
    +    }
    +
    +    private static ComponentContext mockedContext() {
    +      final ComponentContext mock = mock(ComponentContext.class);
    +      when(mock.getCurrentComponentName()).thenReturn("unknown");
    +      return mock;
    +    }
    +
    +    private void setupDefaultLoggedUser() {
    +      when(loggedUser.getId()).thenReturn("2");
    +      when(loggedUser.getDomainId()).thenReturn(LOGGED_USER_DOMAIN_ID);
    +      when(loggedUser.getAccessLevel()).thenReturn(USER);
    +    }
    +
    +    public void setManageableSpaceIds(String... spaceIds) {
    +      final List<String> allIds = new ArrayList<>();
    +      List<String> currents = List.of(spaceIds);
    +      while (!currents.isEmpty()) {
    +        allIds.addAll(currents);
    +        currents = SPACE_SPACE.entrySet()
    +            .stream()
    +            .filter(e -> allIds.contains(e.getValue()))
    +            .map(Map.Entry::getKey)
    +            .filter(not(allIds::contains))
    +            .collect(Collectors.toList());
    +      }
    +      manageableSpaceIds = allIds.toArray(new String[0]);
    +    }
    +
    +    @Override
    +    public UserDetail getUserDetail() {
    +      return loggedUser;
    +    }
    +
    +    @Override
    +    protected String[] getUserManageableSpaceIds() {
    +      return manageableSpaceIds;
    +    }
    +  }
    +}
    \ No newline at end of file
    
  • core-web/src/main/java/org/silverpeas/core/webapi/security/CipherKeyResource.java+9 0 modified
    @@ -32,6 +32,7 @@
     import org.silverpeas.core.util.LocalizationBundle;
     import org.silverpeas.core.util.ResourceLocator;
     import org.silverpeas.core.web.rs.RESTWebService;
    +import org.silverpeas.core.web.rs.UserPrivilegeValidation;
     import org.silverpeas.core.web.rs.annotation.Authorized;
     
     import javax.inject.Inject;
    @@ -40,6 +41,7 @@
     import javax.ws.rs.PUT;
     import javax.ws.rs.Path;
     import javax.ws.rs.Produces;
    +import javax.ws.rs.WebApplicationException;
     import javax.ws.rs.core.MediaType;
     import javax.ws.rs.core.Response;
     
    @@ -121,6 +123,13 @@ private ContentEncryptionService getContentEncryptionService() {
         return contentEncryptionService;
       }
     
    +  @Override
    +  public void validateUserAuthorization(final UserPrivilegeValidation validation) {
    +    if (!getUser().isAccessAdmin()) {
    +      throw new WebApplicationException(Response.Status.FORBIDDEN);
    +    }
    +  }
    +
       private static String formatMessage(String pattern, String value) {
         String msg = pattern;
         if (!pattern.endsWith("\n")) {
    
  • core-web/src/main/java/org/silverpeas/core/web/filter/MassiveWebSecurityFilter.java+3 2 modified
    @@ -54,6 +54,7 @@
     import java.util.regex.Matcher;
     import java.util.regex.Pattern;
     
    +import static java.util.Optional.ofNullable;
     import static org.silverpeas.core.util.URLUtil.getCurrentServerURL;
     
     /**
    @@ -136,8 +137,8 @@ public void doFilter(final ServletRequest request, final ServletResponse respons
     
         } catch (WebSecurityException wse) {
     
    -      logger.error("The request for path {0} isn''t valid: {1}", pathOf(httpRequest),
    -          wse.getMessage());
    +      logger.error("The request for path {0} (uid={1}) isn''t valid: {2}", pathOf(httpRequest),
    +          ofNullable(User.getCurrentRequester()).map(User::getId).orElse("N/A"), wse.getMessage());
     
           // An HTTP error is sended to the client
           httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, wse.getMessage());
    
  • core-web/src/main/java/org/silverpeas/core/web/mvc/controller/AbstractAdminComponentSessionController.java+208 0 added
    @@ -0,0 +1,208 @@
    +/*
    + * Copyright (C) 2000 - 2023 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.core.web.mvc.controller;
    +
    +import org.silverpeas.core.admin.component.model.SilverpeasComponentInstance;
    +import org.silverpeas.core.admin.space.SpaceInst;
    +import org.silverpeas.core.admin.space.SpaceInstLight;
    +import org.silverpeas.core.security.authorization.ForbiddenRuntimeException;
    +import org.silverpeas.core.util.StringUtil;
    +import org.silverpeas.core.web.mvc.route.AdminComponentRequestRouter;
    +
    +import java.util.Objects;
    +import java.util.Set;
    +
    +import static java.util.Optional.ofNullable;
    +import static java.util.function.Predicate.not;
    +import static java.util.stream.Collectors.toSet;
    +import static java.util.stream.Stream.of;
    +import static org.silverpeas.core.util.StringUtil.isDefined;
    +
    +/**
    + * This abstraction centralizes common processes dedicated to several
    + * {@link AbstractComponentSessionController} implementations in charge of the management of
    + * administration services.
    + * <p>
    + *   To be fully functional, an implementation of this abstraction MUST be used with an
    + *   {@link AdminComponentRequestRouter} implementation.
    + * </p>
    + * @author silveryocha
    + */
    +public abstract class AbstractAdminComponentSessionController
    +    extends AbstractComponentSessionController {
    +  private static final long serialVersionUID = 866731879572404115L;
    +
    +  public AbstractAdminComponentSessionController(final MainSessionController controller,
    +      final ComponentContext context, final String localizedMessagesBundleName) {
    +    super(controller, context, localizedMessagesBundleName);
    +  }
    +
    +  public AbstractAdminComponentSessionController(final MainSessionController controller,
    +      final ComponentContext context, final String localizedMessagesBundleName,
    +      final String iconFileName) {
    +    super(controller, context, localizedMessagesBundleName, iconFileName);
    +  }
    +
    +  public AbstractAdminComponentSessionController(final MainSessionController controller,
    +      final ComponentContext context, final String localizedMessagesBundleName,
    +      final String iconFileName, final String settingsFileName) {
    +    super(controller, context, localizedMessagesBundleName, iconFileName, settingsFileName);
    +  }
    +
    +  @Override
    +  public void setAppModeMaintenance(final boolean mode) {
    +    checkAdminAccessOnly();
    +    super.setAppModeMaintenance(mode);
    +  }
    +
    +  @Override
    +  public void setSpaceModeMaintenance(final String spaceId, final boolean mode) {
    +    if (isDefined(spaceId)) {
    +      checkAccessGranted(spaceId, null, false);
    +    } else {
    +      throwForbiddenError();
    +    }
    +    super.setSpaceModeMaintenance(spaceId, mode);
    +  }
    +
    +  public boolean isUserAdmin() {
    +    return getUserDetail().isAccessAdmin();
    +  }
    +
    +  /**
    +   * Checks the user has full admin access.
    +   */
    +  public void checkAdminAccessOnly() {
    +    if (!isUserAdmin()) {
    +      throwForbiddenError();
    +    }
    +  }
    +
    +  /**
    +   * Used mainly by {@link #checkAccessGranted()}.
    +   * <p>
    +   *   Each implementation can precise or change this default implementation.
    +   * </p>
    +   * @return true if access granted, false otherwise.
    +   */
    +  public boolean isAccessGranted() {
    +    return isUserAdmin();
    +  }
    +
    +  /**
    +   * This method is invoked at each administration service access.
    +   * @throws ForbiddenRuntimeException in case the user has forbidden access.
    +   */
    +  public void checkAccessGranted() {
    +    if (!isAccessGranted()) {
    +      throwForbiddenError();
    +    }
    +  }
    +
    +  /**
    +   * Indicates if the user can access administration about a space or a component instance.
    +   * <p>
    +   *   Whatever the parameters, user having admin access right on its account is always access
    +   *   granted.
    +   * </p>
    +   * <p>
    +   *   If both space id and instance id are given, they are both verified.
    +   * </p>
    +   * <p>
    +   *   If neither space if neither instance id are given, it is verified that current user is a
    +   *   space manager.
    +   * </p>
    +   * @param spaceId the optional identifier of a space.
    +   * @param instanceId the optional identifier of a component instance.
    +   * @param readOnly true if the operation is read only, false otherwise.
    +   * @return true if access granted, false otherwise.
    +   */
    +  public boolean isAccessGranted(final String spaceId, final String instanceId,
    +      final boolean readOnly)
    +      throws ForbiddenRuntimeException {
    +    boolean accessGranted = getUserDetail().isAccessAdmin();
    +    if (!accessGranted) {
    +      final String[] userManageableSpaceIds = getUserManageableSpaceIds();
    +      accessGranted = userManageableSpaceIds.length > 0 &&
    +          (readOnly || isDefined(spaceId) || isDefined(instanceId));
    +      if (accessGranted) {
    +        final Set<String> userManageableSpaceIdSet = of(userManageableSpaceIds).collect(toSet());
    +        if (isDefined(instanceId)) {
    +          accessGranted = getOrganisationController().getComponentInstance(instanceId)
    +              .map(SilverpeasComponentInstance::getSpaceId)
    +              .map(userManageableSpaceIdSet::contains)
    +              .orElse(false);
    +        }
    +        if (accessGranted && isDefined(spaceId)) {
    +          accessGranted = ofNullable(getOrganisationController().getSpaceInstById(spaceId))
    +              .map(SpaceInst::getId)
    +              .map(s -> userManageableSpaceIdSet.contains(s) ||
    +                  (readOnly && of(userManageableSpaceIds)
    +                      .map(getOrganisationController()::getSpaceInstById)
    +                      .filter(Objects::nonNull)
    +                      .map(SpaceInst::getDomainFatherId)
    +                      .filter(StringUtil::isDefined)
    +                      .filter(not(userManageableSpaceIdSet::contains))
    +                      .flatMap(m -> getOrganisationController().getPathToSpace(m).stream())
    +                      .map(SpaceInstLight::getId)
    +                      .anyMatch(s::equals)))
    +              .orElse(false);
    +        }
    +      }
    +    }
    +    return accessGranted;
    +  }
    +
    +  /**
    +   * Checks the user can access administration about a space or a component instance.
    +   * <p>
    +   *   Whatever the parameters, user having admin access right on its account is always access
    +   *   granted.
    +   * </p>
    +   * <p>
    +   *   If both space id and instance id are given, they are both verified.
    +   * </p>
    +   * <p>
    +   *   If neither space if neither instance id are given, it is verified that current user is a
    +   *   space manager.
    +   * </p>
    +   * @param spaceId the optional identifier of a space.
    +   * @param instanceId the optional identifier of a component instance.
    +   * @param readOnly true if the operation is read only, false otherwise.
    +   * @throws ForbiddenRuntimeException in case the user has forbidden access.
    +   */
    +  public void checkAccessGranted(final String spaceId, final String instanceId,
    +      final boolean readOnly)
    +      throws ForbiddenRuntimeException {
    +    if (!isAccessGranted(spaceId, instanceId, readOnly)) {
    +      throwForbiddenError();
    +    }
    +  }
    +
    +  protected void throwForbiddenError() {
    +    throw new ForbiddenRuntimeException(
    +        String.format("Forbidden admin access to user with id %s", getUserId()));
    +  }
    +}
    
  • core-web/src/main/java/org/silverpeas/core/web/mvc/route/AdminComponentRequestRouter.java+84 0 added
    @@ -0,0 +1,84 @@
    +/*
    + * Copyright (C) 2000 - 2023 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.core.web.mvc.route;
    +
    +import org.silverpeas.core.security.authorization.ForbiddenRuntimeException;
    +import org.silverpeas.core.util.Mutable;
    +import org.silverpeas.core.util.StringUtil;
    +import org.silverpeas.core.web.http.HttpRequest;
    +import org.silverpeas.core.web.mvc.controller.AbstractAdminComponentSessionController;
    +
    +import static java.util.Optional.ofNullable;
    +
    +/**
    + * This abstraction is dedicated to administration implementations of
    + * {@link ComponentRequestRouter}.
    + * <p>
    + * It mainly centralizes useful common processes for administration context, as the management of
    + * forbidden access.
    + * </p>
    + * @author silveryocha
    + */
    +public abstract class AdminComponentRequestRouter<T extends AbstractAdminComponentSessionController>
    +    extends ComponentRequestRouter<T> {
    +  private static final long serialVersionUID = -1845547120139170876L;
    +
    +  /**
    +   * This method has to be implemented by the admin component request Router it has to compute a
    +   * destination page
    +   * @param function The entering request function (ex : "Main.jsp", when accessing
    +   * "/RjobManager/jsp/Main.jsp")
    +   * @param componentSC The component Session Controller, build and initialised.
    +   * @param request The entering request. The request Router need it to get parameters
    +   * @return The complete destination URL for a forward (ex :
    +   * "/RjobManager/jsp/Main.jsp?flag=read")
    +   */
    +  public abstract String getAdminDestination(String function, T componentSC, HttpRequest request);
    +
    +  @Override
    +  public String getDestination(final String function, final T componentSC,
    +      final HttpRequest request) {
    +    String destination = StringUtil.EMPTY;
    +    final Mutable<ForbiddenRuntimeException> forbidden = Mutable.empty();
    +    try {
    +      componentSC.checkAccessGranted();
    +      try {
    +        destination = getAdminDestination(function, componentSC, request);
    +      } finally {
    +        ofNullable(request.getAttribute("javax.servlet.jsp.jspException"))
    +            .filter(ForbiddenRuntimeException.class::isInstance)
    +            .map(ForbiddenRuntimeException.class::cast)
    +            .stream()
    +            .forEach(forbidden::set);
    +      }
    +    } catch (final ForbiddenRuntimeException e) {
    +      forbidden.set(e);
    +    }
    +    if (forbidden.isPresent()) {
    +      throwHttpForbiddenError(forbidden.get().getMessage());
    +    }
    +    return destination;
    +  }
    +}
    
  • core-web/src/main/java/org/silverpeas/core/web/mvc/webcomponent/SilverpeasHttpServlet.java+8 1 modified
    @@ -58,7 +58,14 @@ protected void service(final HttpServletRequest request, final HttpServletRespon
           super.service(request, response);
     
         } catch (HttpError httpError) {
    -      SilverLogger.getLogger(this).debug("http error " + httpError.errorCode, httpError);
    +      final String msg = String.format("http error %s [%s] -> %s", httpError.errorCode,
    +          request.getRequestURI(), httpError.message);
    +      final SilverLogger logger = SilverLogger.getLogger(this);
    +      if (HttpServletResponse.SC_FORBIDDEN == httpError.errorCode) {
    +        logger.error(msg);
    +      } else {
    +        logger.debug(msg);
    +      }
           httpError.performResponse(response);
         }
       }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.