From 39a2e0849f84a294b850430a6d0e5415aa0e2bee Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 27 Oct 2023 18:22:26 +0100 Subject: [PATCH] feat: Make Programs and rules publically accessible when Hub access is Open - MEED-2811 - Meeds-io/MIPs#100 (#1275) This change will introduce new business rules to allow open the access to program details and its associated rules when the Hub access is open. At the same time to have a better performances to request the programs, the Space Registration startegy is duplicated in Program Table and thus a migration procedure has been elaborated to make the change on existing programs. --- .../gamification/upgrade-configuration.xml | 23 ++ .../WEB-INF/jsp/engagementCenterActions.jsp | 9 +- .../webapp/WEB-INF/jsp/programsOverview.jsp | 11 + .../index.jsp => rulesOverview.jsp} | 3 + .../webapp/WEB-INF/jsp/topChallengers.jsp | 10 + portlets/src/main/webapp/WEB-INF/portlet.xml | 14 +- .../main/webapp/html/programsOverview.html | 7 - .../src/main/webapp/html/topChallengers.html | 7 - .../components/GamificationRank.vue | 28 ++- .../components/UsersLeaderboard.vue | 2 +- .../constant/EntityVisibility.java | 26 +++ .../io/meeds/gamification/dao/ProgramDAO.java | 8 +- .../io/meeds/gamification/dao/RuleDAO.java | 9 +- .../gamification/entity/ProgramEntity.java | 30 ++- .../listener/ProgramSpaceListener.java | 33 ++- .../meeds/gamification/model/ProgramDTO.java | 55 ++--- .../gamification/model/UserInfoContext.java | 2 + .../plugin/ProgramTranslationPlugin.java | 2 +- .../plugin/RuleActivityTypePlugin.java | 2 +- .../plugin/RuleTranslationPlugin.java | 2 +- .../rest/LeaderboardEndpoint.java | 70 +++--- .../meeds/gamification/rest/ProgramRest.java | 35 ++- .../gamification/rest/RealizationRest.java | 3 +- .../io/meeds/gamification/rest/RuleRest.java | 31 ++- .../rest/builder/ProgramBuilder.java | 41 ++-- .../rest/model/ProgramRestEntity.java | 7 +- .../model/ProgramWithRulesRestEntity.java | 3 +- .../gamification/service/ProgramService.java | 206 +++++++++--------- .../service/impl/ProgramServiceImpl.java | 64 ++++-- .../service/impl/RealizationServiceImpl.java | 10 +- .../service/impl/RuleServiceImpl.java | 7 +- .../storage/mapper/ProgramMapper.java | 7 + .../ProgramVisibilityUpgradePlugin.java | 121 ++++++++++ .../io/meeds/gamification/utils/Utils.java | 20 +- .../gamification.db.changelog-1.0.0.xml | 14 ++ .../GamificationSpaceListenerTest.java | 53 +++-- .../gamification/mock/SpaceServiceMock.java | 19 +- .../plugin/RuleActivityTypePluginTest.java | 4 +- .../gamification/rest/TestProgramRest.java | 106 +++++++-- .../rest/TestRealizationRest.java | 8 + .../meeds/gamification/rest/TestRuleRest.java | 194 ++++++++++++----- .../service/ProgramServiceTest.java | 31 ++- .../service/RealizationServiceMockTest.java | 5 +- .../test/AbstractServiceTest.java | 1 + .../test/InitContainerTestSuite.java | 2 + .../ProgramVisibilityUpgradePluginTest.java | 71 ++++++ 46 files changed, 1026 insertions(+), 390 deletions(-) create mode 100644 portlets/src/main/webapp/WEB-INF/jsp/programsOverview.jsp rename portlets/src/main/webapp/WEB-INF/jsp/{rulesOverview/index.jsp => rulesOverview.jsp} (92%) create mode 100644 portlets/src/main/webapp/WEB-INF/jsp/topChallengers.jsp delete mode 100644 portlets/src/main/webapp/html/programsOverview.html delete mode 100644 portlets/src/main/webapp/html/topChallengers.html create mode 100644 services/src/main/java/io/meeds/gamification/constant/EntityVisibility.java create mode 100644 services/src/main/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePlugin.java create mode 100644 services/src/test/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePluginTest.java diff --git a/portlets/src/main/webapp/WEB-INF/conf/gamification/upgrade-configuration.xml b/portlets/src/main/webapp/WEB-INF/conf/gamification/upgrade-configuration.xml index dbfddc8dfb..e4ac3e1995 100644 --- a/portlets/src/main/webapp/WEB-INF/conf/gamification/upgrade-configuration.xml +++ b/portlets/src/main/webapp/WEB-INF/conf/gamification/upgrade-configuration.xml @@ -76,6 +76,29 @@ + + ProgramVisibilityUpgradePlugin + addUpgradePlugin + io.meeds.gamification.upgrade.ProgramVisibilityUpgradePlugin + An upgrade plugin to set a program visibility switch audience registration type + + + product.group.id + The groupId of the product + org.exoplatform.social + + + plugin.execution.order + The plugin execution order + 5 + + + plugin.upgrade.execute.once + Execute only once, not each version change + true + + + diff --git a/portlets/src/main/webapp/WEB-INF/jsp/engagementCenterActions.jsp b/portlets/src/main/webapp/WEB-INF/jsp/engagementCenterActions.jsp index bcea5e9735..30200b4815 100644 --- a/portlets/src/main/webapp/WEB-INF/jsp/engagementCenterActions.jsp +++ b/portlets/src/main/webapp/WEB-INF/jsp/engagementCenterActions.jsp @@ -21,13 +21,12 @@ <%@ page import="io.meeds.gamification.service.ProgramService"%> <%@ page import="org.exoplatform.container.ExoContainerContext"%> <%@ page import="io.meeds.gamification.utils.Utils" %> -<%@ page import="org.exoplatform.services.security.ConversationState" %> <% -boolean isAdministrator = Utils.isRewardingManager(ConversationState.getCurrent().getIdentity().getUserId()); -boolean isProgramManager = isAdministrator || ExoContainerContext.getService(ProgramService.class).countOwnedPrograms(ConversationState.getCurrent().getIdentity().getUserId()) > 0; + if (Utils.canAccessAnonymousResources()) { + boolean isAdministrator = request.getRemoteUser() != null && Utils.isRewardingManager(request.getRemoteUser()); + boolean isProgramManager = request.getRemoteUser() != null && (isAdministrator || ExoContainerContext.getService(ProgramService.class).countOwnedPrograms(request.getRemoteUser()) > 0); %> -
+<% } %> + diff --git a/portlets/src/main/webapp/WEB-INF/jsp/programsOverview.jsp b/portlets/src/main/webapp/WEB-INF/jsp/programsOverview.jsp new file mode 100644 index 0000000000..d883daffda --- /dev/null +++ b/portlets/src/main/webapp/WEB-INF/jsp/programsOverview.jsp @@ -0,0 +1,11 @@ +<%@page import="io.meeds.gamification.utils.Utils"%> +<% if (Utils.canAccessAnonymousResources()) { %> +
+
+ +
+
+<% } %> + \ No newline at end of file diff --git a/portlets/src/main/webapp/WEB-INF/jsp/rulesOverview/index.jsp b/portlets/src/main/webapp/WEB-INF/jsp/rulesOverview.jsp similarity index 92% rename from portlets/src/main/webapp/WEB-INF/jsp/rulesOverview/index.jsp rename to portlets/src/main/webapp/WEB-INF/jsp/rulesOverview.jsp index e2fc402200..ae70443ac2 100644 --- a/portlets/src/main/webapp/WEB-INF/jsp/rulesOverview/index.jsp +++ b/portlets/src/main/webapp/WEB-INF/jsp/rulesOverview.jsp @@ -18,8 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ %> +<%@page import="io.meeds.gamification.utils.Utils"%> <% Object showLocked = request.getAttribute("showLocked"); + if (Utils.canAccessAnonymousResources()) { %>
@@ -28,3 +30,4 @@
+<% } %> diff --git a/portlets/src/main/webapp/WEB-INF/jsp/topChallengers.jsp b/portlets/src/main/webapp/WEB-INF/jsp/topChallengers.jsp new file mode 100644 index 0000000000..85790d2161 --- /dev/null +++ b/portlets/src/main/webapp/WEB-INF/jsp/topChallengers.jsp @@ -0,0 +1,10 @@ +<%@page import="io.meeds.gamification.utils.Utils"%> +<% if (Utils.canAccessAnonymousResources()) { %> +
+
+ +
+
+<% } %> diff --git a/portlets/src/main/webapp/WEB-INF/portlet.xml b/portlets/src/main/webapp/WEB-INF/portlet.xml index b233fa46da..1ff3f2566c 100644 --- a/portlets/src/main/webapp/WEB-INF/portlet.xml +++ b/portlets/src/main/webapp/WEB-INF/portlet.xml @@ -126,7 +126,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. prefetch.resource.rest - + text/html @@ -258,14 +258,12 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /html/topChallengers.html + /WEB-INF/jsp/topChallengers.jsp prefetch.resource.rest - -1 - PUBLIC text/html @@ -282,7 +280,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /WEB-INF/jsp/rulesOverview/index.jsp + /WEB-INF/jsp/rulesOverview.jsp text/html @@ -306,7 +304,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /html/programsOverview.html + /WEB-INF/jsp/programsOverview.jsp preload.resource.bundles @@ -314,10 +312,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. preload.resource.rest - + - -1 - PUBLIC text/html diff --git a/portlets/src/main/webapp/html/programsOverview.html b/portlets/src/main/webapp/html/programsOverview.html deleted file mode 100644 index 9655b9611a..0000000000 --- a/portlets/src/main/webapp/html/programsOverview.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
- -
-
diff --git a/portlets/src/main/webapp/html/topChallengers.html b/portlets/src/main/webapp/html/topChallengers.html deleted file mode 100644 index 770e9a3f75..0000000000 --- a/portlets/src/main/webapp/html/topChallengers.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
- -
-
diff --git a/portlets/src/main/webapp/vue-app/profileStats/components/GamificationRank.vue b/portlets/src/main/webapp/vue-app/profileStats/components/GamificationRank.vue index 25aabf8402..e5b8a75d14 100644 --- a/portlets/src/main/webapp/vue-app/profileStats/components/GamificationRank.vue +++ b/portlets/src/main/webapp/vue-app/profileStats/components/GamificationRank.vue @@ -68,12 +68,14 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. justify-center align-end>
-
-
- {{ item.rank }} - + offset-x /> {{ item.score }} diff --git a/portlets/src/main/webapp/vue-app/usersLeaderboard/components/UsersLeaderboard.vue b/portlets/src/main/webapp/vue-app/usersLeaderboard/components/UsersLeaderboard.vue index 6792d92baf..3e1b4183ae 100644 --- a/portlets/src/main/webapp/vue-app/usersLeaderboard/components/UsersLeaderboard.vue +++ b/portlets/src/main/webapp/vue-app/usersLeaderboard/components/UsersLeaderboard.vue @@ -196,7 +196,7 @@ export default { }); }, retrievePrograms() { - return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/gamification/programs?type=ALL&sortByBudget=true`, { + return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/gamification/programs?type=ALL`, { credentials: 'include', }).then(resp => resp?.ok && resp.json()) .then(data => { diff --git a/services/src/main/java/io/meeds/gamification/constant/EntityVisibility.java b/services/src/main/java/io/meeds/gamification/constant/EntityVisibility.java new file mode 100644 index 0000000000..e28b87903f --- /dev/null +++ b/services/src/main/java/io/meeds/gamification/constant/EntityVisibility.java @@ -0,0 +1,26 @@ +/** + * + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +package io.meeds.gamification.constant; + +public enum EntityVisibility { + RESTRICTED, OPEN; +} diff --git a/services/src/main/java/io/meeds/gamification/dao/ProgramDAO.java b/services/src/main/java/io/meeds/gamification/dao/ProgramDAO.java index 893fddbddf..c7e9bf9ce4 100644 --- a/services/src/main/java/io/meeds/gamification/dao/ProgramDAO.java +++ b/services/src/main/java/io/meeds/gamification/dao/ProgramDAO.java @@ -34,6 +34,7 @@ import io.meeds.gamification.constant.EntityFilterType; import io.meeds.gamification.constant.EntityStatusType; import io.meeds.gamification.constant.EntityType; +import io.meeds.gamification.constant.EntityVisibility; import io.meeds.gamification.entity.ProgramEntity; import io.meeds.gamification.model.filter.ProgramFilter; @@ -119,6 +120,9 @@ private void addQueryFilterParameters(ProgramFilter filter, TypedQuery qu if (CollectionUtils.isNotEmpty(filter.getSpacesIds())) { query.setParameter("spacesIds", filter.getSpacesIds()); } + if (filter.getOwnerId() == 0 && (CollectionUtils.isNotEmpty(filter.getSpacesIds()) || !filter.isAllSpaces())) { + query.setParameter("openVisibility", EntityVisibility.OPEN); + } EntityFilterType type = filter.getType(); if (type != null && type != EntityFilterType.ALL) { query.setParameter("type", EntityType.valueOf(type.name())); @@ -173,10 +177,10 @@ private void buildPredicates(ProgramFilter filter, List suffixes, List void addQueryFilterParameters(RuleFilter filter, TypedQuery query if (entityFilterType != null && entityFilterType != EntityFilterType.ALL) { query.setParameter("filterType", EntityType.valueOf(filter.getType().name())); } + if ((CollectionUtils.isNotEmpty(filter.getSpaceIds()) && !filter.isExcludeNoSpace()) + || (CollectionUtils.isEmpty(filter.getSpaceIds()) && !filter.isAllSpaces())) { + query.setParameter("openVisibility", EntityVisibility.OPEN); + } } private String getQueryFilterName(String sortField, @@ -311,11 +316,11 @@ private void buildPredicates(RuleFilter filter, List suffixes, List removePrograms(event)); } + @Override + public void spaceRegistrationEdited(SpaceLifeCycleEvent event) { + CompletableFuture.runAsync(() -> updateProgramsVisibility(event)); + } + + @ExoTransactional + public void updateProgramsVisibility(SpaceLifeCycleEvent event) { + String spaceId = event.getSpace().getId(); + List programIds = getSpaceProgramIds(spaceId); + programIds.forEach(programId -> { + try { + ProgramDTO program = programService.getProgramById(programId); + // Force update visibility computing by updating the program + programService.updateProgram(program); + } catch (Exception e) { + LOG.warn("Error updating program with id {} while its space registration with id {} had changed", + programId, + spaceId, + e); + } + }); + } + @ExoTransactional public void removePrograms(SpaceLifeCycleEvent event) { - ProgramFilter spaceProgramsFilter = new ProgramFilter(true); String spaceId = event.getSpace().getId(); - spaceProgramsFilter.setSpacesIds(Collections.singletonList(Long.parseLong(spaceId))); - List programIds = programService.getProgramIds(spaceProgramsFilter, 0, -1); + List programIds = getSpaceProgramIds(spaceId); programIds.forEach(programId -> { try { ProgramDTO program = programService.getProgramById(programId); @@ -65,4 +86,10 @@ public void removePrograms(SpaceLifeCycleEvent event) { }); } + private List getSpaceProgramIds(String spaceId) { + ProgramFilter spaceProgramsFilter = new ProgramFilter(true); + spaceProgramsFilter.setSpacesIds(Collections.singletonList(Long.parseLong(spaceId))); + return programService.getProgramIds(spaceProgramsFilter, 0, -1); + } + } diff --git a/services/src/main/java/io/meeds/gamification/model/ProgramDTO.java b/services/src/main/java/io/meeds/gamification/model/ProgramDTO.java index 0b1e449954..3c55f3c712 100644 --- a/services/src/main/java/io/meeds/gamification/model/ProgramDTO.java +++ b/services/src/main/java/io/meeds/gamification/model/ProgramDTO.java @@ -20,6 +20,8 @@ import java.util.HashSet; import java.util.Set; +import io.meeds.gamification.constant.EntityVisibility; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -29,33 +31,33 @@ @Data public class ProgramDTO implements Serializable, Cloneable { - private static final long serialVersionUID = -8857818632949907592L; + private static final long serialVersionUID = -8857818632949907592L; - protected long id; + protected long id; - protected String title; + protected String title; - protected String description; + protected String description; - protected String color; + protected String color; - protected long spaceId; + protected long spaceId; - protected int priority; + protected int priority; - protected String createdBy; + protected String createdBy; - protected String createdDate; + protected String createdDate; - protected String lastModifiedBy; + protected String lastModifiedBy; - protected String lastModifiedDate; + protected String lastModifiedDate; - protected boolean deleted; + protected boolean deleted; - protected boolean enabled; + protected boolean enabled; - protected long budget; + protected long budget; /** * @deprecated There is no difference anymore between Automatic or Manual @@ -63,25 +65,27 @@ public class ProgramDTO implements Serializable, Cloneable { * used */ @Deprecated(forRemoval = false, since = "1.5.0") - protected String type; + protected String type; + + protected String coverUploadId; - protected String coverUploadId; + protected String avatarUploadId; - protected String avatarUploadId; + protected long coverFileId; - protected long coverFileId; + protected long avatarFileId; - protected long avatarFileId; + protected String coverUrl; - protected String coverUrl; + protected String avatarUrl; - protected String avatarUrl; + protected Set ownerIds; // NOSONAR - protected Set ownerIds; // NOSONAR + protected long rulesTotalScore; - protected long rulesTotalScore; + protected boolean open; - protected boolean open; + protected EntityVisibility visibility; /** * Deprecated should be renamed to spaceId knowing that audienceId should @@ -131,7 +135,8 @@ public ProgramDTO clone() { // NOSONAR avatarUrl, ownerIds == null ? null : new HashSet<>(ownerIds), rulesTotalScore, - open); + open, + visibility); } } diff --git a/services/src/main/java/io/meeds/gamification/model/UserInfoContext.java b/services/src/main/java/io/meeds/gamification/model/UserInfoContext.java index 76e5c76dd7..eff0b29875 100644 --- a/services/src/main/java/io/meeds/gamification/model/UserInfoContext.java +++ b/services/src/main/java/io/meeds/gamification/model/UserInfoContext.java @@ -29,6 +29,8 @@ @ToString(callSuper = true) public class UserInfoContext extends UserInfo { + private boolean canView; + private boolean canEdit; private boolean isAllowedToRealize; diff --git a/services/src/main/java/io/meeds/gamification/plugin/ProgramTranslationPlugin.java b/services/src/main/java/io/meeds/gamification/plugin/ProgramTranslationPlugin.java index 8f53124d78..e68f64fb51 100644 --- a/services/src/main/java/io/meeds/gamification/plugin/ProgramTranslationPlugin.java +++ b/services/src/main/java/io/meeds/gamification/plugin/ProgramTranslationPlugin.java @@ -58,7 +58,7 @@ public String getObjectType() { @Override public boolean hasAccessPermission(long programId, String username) throws ObjectNotFoundException { - return programService.isProgramMember(programId, username); + return programService.canViewProgram(programId, username); } @Override diff --git a/services/src/main/java/io/meeds/gamification/plugin/RuleActivityTypePlugin.java b/services/src/main/java/io/meeds/gamification/plugin/RuleActivityTypePlugin.java index fad6b15e8e..dc13dba95a 100644 --- a/services/src/main/java/io/meeds/gamification/plugin/RuleActivityTypePlugin.java +++ b/services/src/main/java/io/meeds/gamification/plugin/RuleActivityTypePlugin.java @@ -45,7 +45,7 @@ public boolean isActivityViewable(ExoSocialActivity activity, Identity userAclId if (rule == null) { throw new UnsupportedOperationException(); } else { - return programService.isProgramMember(rule.getProgramId(), userAclIdentity.getUserId()); + return programService.canViewProgram(rule.getProgramId(), userAclIdentity.getUserId()); } } diff --git a/services/src/main/java/io/meeds/gamification/plugin/RuleTranslationPlugin.java b/services/src/main/java/io/meeds/gamification/plugin/RuleTranslationPlugin.java index 88e3090198..045f33a990 100644 --- a/services/src/main/java/io/meeds/gamification/plugin/RuleTranslationPlugin.java +++ b/services/src/main/java/io/meeds/gamification/plugin/RuleTranslationPlugin.java @@ -65,7 +65,7 @@ public String getObjectType() { public boolean hasAccessPermission(long ruleId, String username) throws ObjectNotFoundException { try { RuleDTO rule = this.ruleService.findRuleById(ruleId, username); - return rule != null && programService.isProgramMember(rule.getProgramId(), username); + return rule != null && programService.canViewProgram(rule.getProgramId(), username); } catch (IllegalAccessException e) { return false; } diff --git a/services/src/main/java/io/meeds/gamification/rest/LeaderboardEndpoint.java b/services/src/main/java/io/meeds/gamification/rest/LeaderboardEndpoint.java index efe4155266..0b26cd5469 100644 --- a/services/src/main/java/io/meeds/gamification/rest/LeaderboardEndpoint.java +++ b/services/src/main/java/io/meeds/gamification/rest/LeaderboardEndpoint.java @@ -34,6 +34,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang3.StringUtils; @@ -56,44 +57,50 @@ import io.meeds.gamification.model.StandardLeaderboard; import io.meeds.gamification.model.filter.LeaderboardFilter; import io.meeds.gamification.service.RealizationService; +import io.meeds.gamification.utils.Utils; +import io.meeds.portal.security.service.SecuritySettingService; + import io.swagger.v3.oas.annotations.Parameter; @Path("/gamification/leaderboard") @Produces(MediaType.APPLICATION_JSON) -@RolesAllowed("users") public class LeaderboardEndpoint implements ResourceContainer { - private static final Log LOG = ExoLogger.getLogger(LeaderboardEndpoint.class); + private static final Log LOG = ExoLogger.getLogger(LeaderboardEndpoint.class); + + private static final String YOUR_CURRENT_RANK_MSG = "Your current rank"; - private static final String YOUR_CURRENT_RANK_MSG = "Your current rank"; + private static final int DEFAULT_LOAD_CAPACITY = 10; - private static final int DEFAULT_LOAD_CAPACITY = 10; + private static final int MAX_LOAD_CAPACITY = 100; - private static final int MAX_LOAD_CAPACITY = 100; + protected IdentityManager identityManager = null; - protected IdentityManager identityManager = null; + protected RealizationService realizationService = null; - protected RealizationService realizationService = null; + protected RelationshipManager relationshipManager; - protected RelationshipManager relationshipManager; + protected SpaceService spaceService; - protected SpaceService spaceService; + protected SecuritySettingService securitySettingService; public LeaderboardEndpoint(IdentityManager identityManager, RealizationService realizationService, RelationshipManager relationshipManager, - SpaceService spaceService) { + SpaceService spaceService, + SecuritySettingService securitySettingService) { this.identityManager = identityManager; this.realizationService = realizationService; this.relationshipManager = relationshipManager; this.spaceService = spaceService; + this.securitySettingService = securitySettingService; } @GET @Path("rank/all") - @RolesAllowed("users") - public Response getAllLeadersByRank(@Context - UriInfo uriInfo, + public Response getAllLeadersByRank( + @Context + UriInfo uriInfo, @Parameter(description = "Get leaderboard of user or space") @DefaultValue("user") @QueryParam("earnerType") @@ -110,6 +117,9 @@ public Response getAllLeadersByRank(@Context @DefaultValue("true") @QueryParam("loadCapacity") boolean loadCapacity) { + if (!Utils.canAccessAnonymousResources(securitySettingService)) { + return Response.status(Status.UNAUTHORIZED).build(); + } LeaderboardFilter leaderboardFilter = new LeaderboardFilter(); IdentityType identityType = IdentityType.getType(earnerType); leaderboardFilter.setIdentityType(identityType); @@ -125,7 +135,8 @@ public Response getAllLeadersByRank(@Context period = Period.ALL.name(); } leaderboardFilter.setPeriod(period); - leaderboardFilter.setCurrentUser(ConversationState.getCurrent().getIdentity().getUserId()); + String currentUser = Utils.getCurrentUser(); + leaderboardFilter.setCurrentUser(currentUser); List leaderboardList = new ArrayList<>(); @@ -135,21 +146,24 @@ public Response getAllLeadersByRank(@Context return Response.ok(leaderboardList, MediaType.APPLICATION_JSON).build(); } int index = 1; + boolean isAnonymous = StringUtils.isBlank(currentUser); for (StandardLeaderboard element : standardLeaderboards) { Identity identity = identityManager.getIdentity(element.getEarnerId()); if (identity == null) { continue; } LeaderboardInfo leaderboardInfo = new LeaderboardInfo(); - leaderboardInfo.setSocialId(identity.getId()); - String technicalId = computeTechnicalId(identity); - leaderboardInfo.setTechnicalId(technicalId); - leaderboardInfo.setScore(element.getReputationScore()); - leaderboardInfo.setRemoteId(identity.getRemoteId()); leaderboardInfo.setFullname(identity.getProfile().getFullName()); leaderboardInfo.setAvatarUrl(identity.getProfile().getAvatarUrl()); - leaderboardInfo.setProfileUrl(identity.getProfile().getUrl()); + leaderboardInfo.setScore(element.getReputationScore()); leaderboardInfo.setRank(index); + if (!isAnonymous) { + leaderboardInfo.setRemoteId(identity.getRemoteId()); + leaderboardInfo.setSocialId(identity.getId()); + String technicalId = computeTechnicalId(identity); + leaderboardInfo.setTechnicalId(technicalId); + leaderboardInfo.setProfileUrl(identity.getProfile().getUrl()); + } leaderboardList.add(leaderboardInfo); index++; } @@ -168,13 +182,15 @@ public Response getAllLeadersByRank(@Context break; } - // Check if the current user is already in top10 - LeaderboardInfo leader = buildCurrentUserRank(date, - leaderboardFilter.getProgramId(), - leaderboardList); - // Complete the final leaderboard - if (leader != null) { - leaderboardList.add(leader); + if (!isAnonymous) { + // Check if the current user is already in top10 + LeaderboardInfo leader = buildCurrentUserRank(date, + leaderboardFilter.getProgramId(), + leaderboardList); + // Complete the final leaderboard + if (leader != null) { + leaderboardList.add(leader); + } } } return Response.ok(leaderboardList, MediaType.APPLICATION_JSON).build(); diff --git a/services/src/main/java/io/meeds/gamification/rest/ProgramRest.java b/services/src/main/java/io/meeds/gamification/rest/ProgramRest.java index 85f4df1837..5db2b0c965 100644 --- a/services/src/main/java/io/meeds/gamification/rest/ProgramRest.java +++ b/services/src/main/java/io/meeds/gamification/rest/ProgramRest.java @@ -67,6 +67,8 @@ import io.meeds.gamification.rest.model.ProgramRestEntity; import io.meeds.gamification.service.ProgramService; import io.meeds.gamification.service.RuleService; +import io.meeds.gamification.utils.Utils; +import io.meeds.portal.security.service.SecuritySettingService; import io.meeds.social.translation.service.TranslationService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -100,37 +102,40 @@ public class ProgramRest implements ResourceContainer { CACHECONTROL.setPrivate(false); } - protected PortalContainer portalContainer; + protected PortalContainer portalContainer; - protected ProgramService programService; + protected ProgramService programService; - protected RuleService ruleService; + protected RuleService ruleService; - protected IdentityManager identityManager; + protected IdentityManager identityManager; - protected TranslationService translationService; + protected TranslationService translationService; - public byte[] defaultProgramCover = null; // NOSONAR + protected SecuritySettingService securitySettingService; - public byte[] defaultProgramAvatar = null; // NOSONAR + public byte[] defaultProgramCover = null; // NOSONAR + + public byte[] defaultProgramAvatar = null; // NOSONAR public ProgramRest(PortalContainer portalContainer, ProgramService programService, RuleService ruleService, TranslationService translationService, - IdentityManager identityManager) { + IdentityManager identityManager, + SecuritySettingService securitySettingService) { this.portalContainer = portalContainer; this.programService = programService; this.ruleService = ruleService; this.translationService = translationService; this.identityManager = identityManager; + this.securitySettingService = securitySettingService; } @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN }) - @RolesAllowed("users") @Operation(summary = "Retrieves the list of available programs", method = "GET") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), @@ -185,6 +190,9 @@ public Response getPrograms( @Parameter(description = "Used to retrieve extra information about the program") @QueryParam("expand") String expand) { + if (!Utils.canAccessAnonymousResources(securitySettingService)) { + return Response.status(Status.UNAUTHORIZED).build(); + } ProgramFilter programFilter = new ProgramFilter(); programFilter.setSortByBudget(sortByBudget); @@ -196,7 +204,11 @@ public Response getPrograms( programFilter.setProgramTitle(query); } if (owned) { - programFilter.setOwnerId(getCurrentUserIdentityId()); + long currentUserIdentityId = getCurrentUserIdentityId(); + if (currentUserIdentityId == 0) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + programFilter.setOwnerId(currentUserIdentityId); } List expandFields = getExpandOptions(expand); @@ -536,7 +548,6 @@ public Response deleteProgramAvatar( @GET @Produces(MediaType.APPLICATION_JSON) @Path("{programId}") - @RolesAllowed("users") @Operation(summary = "Retrieves a program by its technical identifier", method = "GET") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), @@ -559,6 +570,8 @@ public Response getProgramById( String expand) { if (programId == 0) { return Response.status(Response.Status.BAD_REQUEST).entity("Program Id must be not null").build(); + } else if (!Utils.canAccessAnonymousResources(securitySettingService)) { + return Response.status(Status.UNAUTHORIZED).build(); } String currentUser = getCurrentUser(); try { diff --git a/services/src/main/java/io/meeds/gamification/rest/RealizationRest.java b/services/src/main/java/io/meeds/gamification/rest/RealizationRest.java index c7e5f84579..5d48208b98 100644 --- a/services/src/main/java/io/meeds/gamification/rest/RealizationRest.java +++ b/services/src/main/java/io/meeds/gamification/rest/RealizationRest.java @@ -143,7 +143,8 @@ public Response getRealizations( return Response.status(Response.Status.BAD_REQUEST).entity("Either use limit or dates to limit returned results").build(); } - Identity identity = ConversationState.getCurrent().getIdentity(); + ConversationState conversationState = ConversationState.getCurrent(); + Identity identity = conversationState == null ? null : conversationState.getIdentity(); Date fromDate = Utils.parseRFC3339Date(fromDateRfc3339); Date toDate = Utils.parseRFC3339Date(toDateRfc3339); diff --git a/services/src/main/java/io/meeds/gamification/rest/RuleRest.java b/services/src/main/java/io/meeds/gamification/rest/RuleRest.java index 82a729be1c..ec0950a5ca 100644 --- a/services/src/main/java/io/meeds/gamification/rest/RuleRest.java +++ b/services/src/main/java/io/meeds/gamification/rest/RuleRest.java @@ -67,6 +67,8 @@ import io.meeds.gamification.service.ProgramService; import io.meeds.gamification.service.RealizationService; import io.meeds.gamification.service.RuleService; +import io.meeds.gamification.utils.Utils; +import io.meeds.portal.security.service.SecuritySettingService; import io.meeds.social.translation.service.TranslationService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -80,26 +82,29 @@ @Tag(name = "/gamification/rules", description = "Manages rules") public class RuleRest implements ResourceContainer { - private final CacheControl cacheControl; + private final CacheControl cacheControl; - protected ProgramService programService; + protected ProgramService programService; - protected RuleService ruleService; + protected RuleService ruleService; - protected RealizationService realizationService; + protected RealizationService realizationService; - protected TranslationService translationService; + protected TranslationService translationService; - protected FavoriteService favoriteService; + protected FavoriteService favoriteService; - protected IdentityManager identityManager; + protected IdentityManager identityManager; + + protected SecuritySettingService securitySettingService; public RuleRest(ProgramService programService, RuleService ruleService, RealizationService realizationService, TranslationService translationService, FavoriteService favoriteService, - IdentityManager identityManager) { + IdentityManager identityManager, + SecuritySettingService securitySettingService) { cacheControl = new CacheControl(); cacheControl.setNoCache(true); cacheControl.setNoStore(true); @@ -109,11 +114,11 @@ public RuleRest(ProgramService programService, this.translationService = translationService; this.favoriteService = favoriteService; this.identityManager = identityManager; + this.securitySettingService = securitySettingService; } @GET @Produces(MediaType.APPLICATION_JSON) - @RolesAllowed("users") @Operation(summary = "Retrieves the list of available rules", method = "GET") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), @@ -210,6 +215,9 @@ public Response getRules(// NOSONAR if (limit < 0) { return Response.status(Response.Status.BAD_REQUEST).entity("Limit must be positive").build(); } + if (!Utils.canAccessAnonymousResources(securitySettingService)) { + return Response.status(Status.UNAUTHORIZED).build(); + } Locale locale = getLocale(lang); @@ -286,7 +294,6 @@ public Response getRules(// NOSONAR @GET @Produces(MediaType.APPLICATION_JSON) - @RolesAllowed("users") @Path("{id}") @Operation(summary = "Retrieves the list of available rules", method = "GET") @ApiResponses(value = { @@ -311,6 +318,10 @@ public Response getRule( @Parameter(description = "Asking for a full representation of a specific subresource, ex: countRealizations", required = false) @QueryParam("expand") String expand) { + if (!Utils.canAccessAnonymousResources(securitySettingService)) { + return Response.status(Status.UNAUTHORIZED).build(); + } + String currentUser = getCurrentUser(); try { RuleDTO rule = ruleService.findRuleById(id, currentUser); diff --git a/services/src/main/java/io/meeds/gamification/rest/builder/ProgramBuilder.java b/services/src/main/java/io/meeds/gamification/rest/builder/ProgramBuilder.java index 1bb33c5cf1..01592348c9 100644 --- a/services/src/main/java/io/meeds/gamification/rest/builder/ProgramBuilder.java +++ b/services/src/main/java/io/meeds/gamification/rest/builder/ProgramBuilder.java @@ -101,7 +101,8 @@ public static ProgramRestEntity toRestEntity(ProgramService programService, program.getSpaceId() > 0 ? Utils.getSpaceById(String.valueOf(program.getSpaceId())) : null, toUserContext(programService, program, username), getProgramOwnersByIds(program.getOwnerIds(), program.getSpaceId()), - activeRulesCount); + activeRulesCount, + program.getVisibility()); } public static void translatedLabels(TranslationService translationService, ProgramDTO program, Locale locale) { @@ -177,29 +178,27 @@ public static UserInfoContext toUserContext(ProgramService programService, UserInfoContext userContext = new UserInfoContext(); IdentityManager identityManager = CommonsUtils.getService(IdentityManager.class); - Identity identity = identityManager.getOrCreateUserIdentity(username); + Identity identity = StringUtils.isBlank(username) ? null : identityManager.getOrCreateUserIdentity(username); mapUserInfo(userContext, identity); if (program != null) { boolean isOwner = programService.isProgramOwner(program.getId(), username); + boolean isMember = programService.isProgramMember(program.getId(), username); + boolean canView = programService.canViewProgram(program.getId(), username); + boolean canEdit = isOwner && !program.isDeleted(); + userContext.setManager(isOwner); + userContext.setCanEdit(canEdit); + userContext.setMember(isMember); + userContext.setCanView(canView); + userContext.setProgramOwner(isOwner); + if (program.isOpen()) { - boolean isMember = programService.isProgramMember(program.getId(), username); - userContext.setManager(isOwner); - userContext.setCanEdit(isOwner); - userContext.setMember(isMember); - userContext.setProgramOwner(isOwner); userContext.setRedactor(true); } else { Space space = Utils.getSpaceById(String.valueOf(program.getSpaceId())); if (space != null) { - SpaceService spaceService = CommonsUtils.getService(SpaceService.class); - boolean isSuperManager = spaceService.isSuperManager(username); - boolean isManager = isSuperManager || spaceService.isManager(space, username); - boolean isMember = isManager || spaceService.isMember(space, username); - boolean isRedactor = isManager || spaceService.isRedactor(space, username); - userContext.setManager(isManager); - userContext.setMember(isMember); - userContext.setProgramOwner(isOwner); - userContext.setCanEdit(isOwner && !program.isDeleted()); + boolean isRedactor = StringUtils.isNotBlank(username) + && CommonsUtils.getService(SpaceService.class) + .canRedactOnSpace(space, Utils.getUserAclIdentity(username)); userContext.setRedactor(isRedactor); } } @@ -214,10 +213,12 @@ private static UserInfo toUserInfo(Identity identity) { } private static void mapUserInfo(E userInfo, Identity identity) { - userInfo.setAvatarUrl(identity.getProfile().getAvatarUrl()); - userInfo.setFullName(identity.getProfile().getFullName()); - userInfo.setRemoteId(identity.getRemoteId()); - userInfo.setId(identity.getId()); + if (identity != null) { + userInfo.setAvatarUrl(identity.getProfile().getAvatarUrl()); + userInfo.setFullName(identity.getProfile().getFullName()); + userInfo.setRemoteId(identity.getRemoteId()); + userInfo.setId(identity.getId()); + } } } diff --git a/services/src/main/java/io/meeds/gamification/rest/model/ProgramRestEntity.java b/services/src/main/java/io/meeds/gamification/rest/model/ProgramRestEntity.java index 7192aebbb8..c0284d6519 100644 --- a/services/src/main/java/io/meeds/gamification/rest/model/ProgramRestEntity.java +++ b/services/src/main/java/io/meeds/gamification/rest/model/ProgramRestEntity.java @@ -22,6 +22,7 @@ import org.exoplatform.social.core.space.model.Space; +import io.meeds.gamification.constant.EntityVisibility; import io.meeds.gamification.model.ProgramDTO; import io.meeds.gamification.model.UserInfo; import lombok.AllArgsConstructor; @@ -73,7 +74,8 @@ public ProgramRestEntity(long id, // NOSONAR Space space, UserInfo userInfo, List owners, - int activeRulesCount) { + int activeRulesCount, + EntityVisibility visibility) { super(id, title, description, @@ -96,7 +98,8 @@ public ProgramRestEntity(long id, // NOSONAR avatarUrl, ownerIds, rulesTotalScore, - open); + open, + visibility); this.space = space; this.userInfo = userInfo; this.owners = owners; diff --git a/services/src/main/java/io/meeds/gamification/rest/model/ProgramWithRulesRestEntity.java b/services/src/main/java/io/meeds/gamification/rest/model/ProgramWithRulesRestEntity.java index b9eb061794..468a2d0572 100644 --- a/services/src/main/java/io/meeds/gamification/rest/model/ProgramWithRulesRestEntity.java +++ b/services/src/main/java/io/meeds/gamification/rest/model/ProgramWithRulesRestEntity.java @@ -61,7 +61,8 @@ public ProgramWithRulesRestEntity(ProgramDTO program) { program.getAvatarUrl(), program.getOwnerIds(), program.getRulesTotalScore(), - program.isOpen()); + program.isOpen(), + program.getVisibility()); } } diff --git a/services/src/main/java/io/meeds/gamification/service/ProgramService.java b/services/src/main/java/io/meeds/gamification/service/ProgramService.java index 71e19b4253..89a1a16ca6 100644 --- a/services/src/main/java/io/meeds/gamification/service/ProgramService.java +++ b/services/src/main/java/io/meeds/gamification/service/ProgramService.java @@ -40,13 +40,13 @@ public interface ProgramService { /** * Gets programs by filter. * - * @param programFilter {@link ProgramFilter} used to filter results - * @param username User name accessing programs - * @param offset index of the search - * @param limit limit of results to return - * @return A {@link List <ProgramDTO>} object + * @param programFilter {@link ProgramFilter} used to filter results + * @param username User name accessing programs + * @param offset index of the search + * @param limit limit of results to return + * @return A {@link List <ProgramDTO>} object * @throws IllegalAccessException when user is not authorized to get another - * owner's programs list + * owner's programs list */ List getPrograms(ProgramFilter programFilter, String username, @@ -56,13 +56,13 @@ List getPrograms(ProgramFilter programFilter, /** * Gets Program Ids by filter. * - * @param programFilter {@link ProgramFilter} used to filter results - * @param username User name accessing Programs - * @param offset index of the search - * @param limit limit of results to return - * @return A {@link List <ProgramDTO>} object + * @param programFilter {@link ProgramFilter} used to filter results + * @param username User name accessing Programs + * @param offset index of the search + * @param limit limit of results to return + * @return A {@link List <ProgramDTO>} object * @throws IllegalAccessException when user is not authorized to get another - * owner's Programs list + * owner's Programs list */ List getProgramIds(ProgramFilter programFilter, String username, @@ -72,88 +72,84 @@ List getProgramIds(ProgramFilter programFilter, /** * Gets Program Ids by filter. * - * @param programFilter {@link ProgramFilter} used to filter results - * @param offset index of the search - * @param limit limit of results to return - * @return A {@link List <ProgramDTO>} object + * @param programFilter {@link ProgramFilter} used to filter results + * @param offset index of the search + * @param limit limit of results to return + * @return A {@link List <ProgramDTO>} object */ List getProgramIds(ProgramFilter programFilter, int offset, int limit); /** - * @param username user name - * @param offset start index for fetch - * @param limit limit to fetch - * @return {@link List} of {@link ProgramDTO} id of programs where - * the user is owner + * @param username user name + * @param offset start index for fetch + * @param limit limit to fetch + * @return {@link List} of {@link ProgramDTO} id of programs where the user is + * owner */ List getOwnedProgramIds(String username, int offset, int limit); /** - * @param username user name - * @param offset start index for fetch - * @param limit limit to fetch - * @return {@link List} of {@link ProgramDTO} id of programs where - * the user is member of + * @param username user name + * @param offset start index for fetch + * @param limit limit to fetch + * @return {@link List} of {@link ProgramDTO} id of programs where the user is + * member of */ List getMemberProgramIds(String username, int offset, int limit); /** * Find a Program by title * - * @param programTitle : Program title - * @return found {@link ProgramDTO} + * @param programTitle : Program title + * @return found {@link ProgramDTO} */ ProgramDTO getProgramByTitle(String programTitle); /** * Creates a new Program * - * @param program : an object of type ProgramDTO - * @param aclIdentity Security identity of user attempting to - * create a program - * @return created {@link ProgramDTO} + * @param program : an object of type ProgramDTO + * @param aclIdentity Security identity of user attempting to create a program + * @return created {@link ProgramDTO} * @throws IllegalAccessException when user is not authorized to create a - * Program for the designated owner defined - * in object + * Program for the designated owner defined in object */ ProgramDTO createProgram(ProgramDTO program, Identity aclIdentity) throws IllegalAccessException; /** * Creates a new Program * - * @param program : an object of type ProgramDTO - * @return created {@link ProgramDTO} + * @param program : an object of type ProgramDTO + * @return created {@link ProgramDTO} */ ProgramDTO createProgram(ProgramDTO program); /** * Update an existing Program * - * @param program : an instance of type ProgramDTO - * @param aclIdentity Security identity of user attempting to - * update a program - * @return updated object {@link ProgramDTO} + * @param program : an instance of type ProgramDTO + * @param aclIdentity Security identity of user attempting to update a program + * @return updated object {@link ProgramDTO} * @throws IllegalArgumentException when user is not authorized to update the - * Program - * @throws ObjectNotFoundException when the Program identified by its - * technical identifier is not found - * @throws IllegalAccessException when user is not authorized to create a - * Program for the designated owner defined - * in object + * Program + * @throws ObjectNotFoundException when the Program identified by its + * technical identifier is not found + * @throws IllegalAccessException when user is not authorized to create a + * Program for the designated owner defined in object */ ProgramDTO updateProgram(ProgramDTO program, Identity aclIdentity) throws ObjectNotFoundException, IllegalAccessException; /** * Update an existing Program * - * @param program : an instance of type ProgramDTO - * @return updated object {@link ProgramDTO} + * @param program : an instance of type ProgramDTO + * @return updated object {@link ProgramDTO} * @throws IllegalArgumentException when user is not authorized to update the - * Program - * @throws ObjectNotFoundException when the Program identified by its - * technical identifier is not found + * Program + * @throws ObjectNotFoundException when the Program identified by its + * technical identifier is not found */ ProgramDTO updateProgram(ProgramDTO program) throws ObjectNotFoundException; @@ -162,12 +158,11 @@ List getProgramIds(ProgramFilter programFilter, /** * Deletes an existing Program by id * - * @param programId Program technical identifier to delete - * @param aclIdentity Security identity of user attempting to - * delete a program - * @return deleted {@link ProgramDTO} - * @throws IllegalAccessException when user is not authorized to delete - * program + * @param programId Program technical identifier to delete + * @param aclIdentity Security identity of user attempting to delete a program + * @return deleted {@link ProgramDTO} + * @throws IllegalAccessException when user is not authorized to delete + * program * @throws ObjectNotFoundException program not found */ ProgramDTO deleteProgramById(long programId, Identity aclIdentity) throws ObjectNotFoundException, IllegalAccessException; // NOSONAR @@ -175,11 +170,11 @@ List getProgramIds(ProgramFilter programFilter, /** * Delete program Cover identified by program id * - * @param programId {@link ProgramDTO} technical identifier - * @param aclIdentity Security identity of user attempting to - * delete the program cover - * @throws IllegalAccessException when user is not authorized to delete - * program cover + * @param programId {@link ProgramDTO} technical identifier + * @param aclIdentity Security identity of user attempting to delete the + * program cover + * @throws IllegalAccessException when user is not authorized to delete + * program cover * @throws ObjectNotFoundException program not found */ void deleteProgramCoverById(long programId, Identity aclIdentity) throws ObjectNotFoundException, IllegalAccessException; // NOSONAR @@ -187,11 +182,11 @@ List getProgramIds(ProgramFilter programFilter, /** * Delete program Avatar identified by program id * - * @param programId {@link ProgramDTO} technical identifier - * @param aclIdentity Security identity of user attempting to - * delete the program avatar - * @throws IllegalAccessException when user is not authorized to delete - * program avatar + * @param programId {@link ProgramDTO} technical identifier + * @param aclIdentity Security identity of user attempting to delete the + * program avatar + * @throws IllegalAccessException when user is not authorized to delete + * program avatar * @throws ObjectNotFoundException program not found */ void deleteProgramAvatarById(long programId, Identity aclIdentity) throws ObjectNotFoundException, IllegalAccessException; @@ -199,8 +194,8 @@ List getProgramIds(ProgramFilter programFilter, /** * Retrieves a program identified by its technical identifier. * - * @param programId : program id - * @return found {@link ProgramDTO} + * @param programId : program id + * @return found {@link ProgramDTO} */ ProgramDTO getProgramById(long programId); @@ -208,11 +203,11 @@ List getProgramIds(ProgramFilter programFilter, * Retrieves a program identified by its technical identifier accessed by a * user * - * @param programId - * @param username - * @return found {@link ProgramDTO} - * @throws IllegalAccessException when user is not authorized to access - * program + * @param programId + * @param username + * @return found {@link ProgramDTO} + * @throws IllegalAccessException when user is not authorized to access + * program * @throws ObjectNotFoundException program not found */ ProgramDTO getProgramById(long programId, String username) throws IllegalAccessException, ObjectNotFoundException; @@ -220,63 +215,59 @@ List getProgramIds(ProgramFilter programFilter, /** * Count all Programs by filter * - * @param programFilter {@link ProgramFilter} used to filter - * Programs - * @param username User name accessing Programs - * @return Programs count + * @param programFilter {@link ProgramFilter} used to filter Programs + * @param username User name accessing Programs + * @return Programs count * @throws IllegalAccessException when user is not authorized to get another - * owner's Programs list + * owner's Programs list */ int countPrograms(ProgramFilter programFilter, String username) throws IllegalAccessException; /** * Count all Programs by filter * - * @param programFilter {@link ProgramFilter} used to filter Programs - * @return Programs count + * @param programFilter {@link ProgramFilter} used to filter Programs + * @return Programs count */ int countPrograms(ProgramFilter programFilter); /** - * @param username User name accessing Programs - * @return Owned Programs count for a given user identified by its - * name + * @param username User name accessing Programs + * @return Owned Programs count for a given user identified by its name */ int countOwnedPrograms(String username); /** - * @param username User name accessing Programs - * @return Programs as member count for a given user identified by - * its name + * @param username User name accessing Programs + * @return Programs as member count for a given user identified by its name */ int countMemberPrograms(String username); /** * Retrieves the program cover identified by Program technical identifier. * - * @param programId Program unique identifier - * @return found {@link InputStream} + * @param programId Program unique identifier + * @return found {@link InputStream} * @throws ObjectNotFoundException When program not found or file attachment - * not found + * not found */ InputStream getProgramCoverStream(long programId) throws ObjectNotFoundException; /** * Retrieves the program avatar identified by Program technical identifier. * - * @param programId Program unique identifier - * @return found {@link InputStream} + * @param programId Program unique identifier + * @return found {@link InputStream} * @throws ObjectNotFoundException When program not found or file attachment - * not found + * not found */ InputStream getProgramAvatarStream(long programId) throws ObjectNotFoundException; /** * Check whether user can add programs or not * - * @param aclIdentity Security identity of user - * @return true if user has enough privileges to create a program, - * else false + * @param aclIdentity Security identity of user + * @return true if user has enough privileges to create a program, else false */ boolean canAddProgram(Identity aclIdentity); @@ -292,21 +283,28 @@ List getProgramIds(ProgramFilter programFilter, /** * Check whether user can add programs or not * - * @param programId technical identifier of program - * @param username user name - * @return true if user has enough privileges to create a program, - * else false + * @param programId technical identifier of program + * @param username user name + * @return true if user has enough privileges to create a program, else false */ boolean isProgramOwner(long programId, String username); /** * Check whether user is member of program or not * - * @param programId technical identifier of program - * @param username user name - * @return true if user has enough privileges to see a program, else - * false + * @param programId technical identifier of program + * @param username user name + * @return true if user has enough privileges to see a program, else false */ boolean isProgramMember(long programId, String username); + /** + * Check whether user can view program details or not + * + * @param programId technical identifier of program + * @param username user name + * @return true if user has enough privileges to see a program, else false + */ + boolean canViewProgram(long programId, String username); + } diff --git a/services/src/main/java/io/meeds/gamification/service/impl/ProgramServiceImpl.java b/services/src/main/java/io/meeds/gamification/service/impl/ProgramServiceImpl.java index 3942dce2b9..7f9caa6d12 100644 --- a/services/src/main/java/io/meeds/gamification/service/impl/ProgramServiceImpl.java +++ b/services/src/main/java/io/meeds/gamification/service/impl/ProgramServiceImpl.java @@ -36,6 +36,7 @@ import org.exoplatform.social.core.space.spi.SpaceService; import io.meeds.gamification.constant.EntityType; +import io.meeds.gamification.constant.EntityVisibility; import io.meeds.gamification.model.ProgramColorAlreadyExists; import io.meeds.gamification.model.ProgramDTO; import io.meeds.gamification.model.filter.ProgramFilter; @@ -48,6 +49,8 @@ public class ProgramServiceImpl implements ProgramService { private static final Log LOG = ExoLogger.getLogger(ProgramServiceImpl.class); + private static final String PROGRAM_DOESN_T_EXIST = "Program doesn't exist"; + protected final ProgramStorage programStorage; protected final ListenerService listenerService; @@ -97,6 +100,9 @@ public List getProgramIds(ProgramFilter programFilter, @Override public List getOwnedProgramIds(String username, int offset, int limit) { + if (StringUtils.isBlank(username)) { + return Collections.emptyList(); + } org.exoplatform.social.core.identity.model.Identity userIdentity = identityManager.getOrCreateUserIdentity(username); long userIdentityId = Long.parseLong(userIdentity.getId()); ProgramFilter programFilter = computeOwnedProgramsFilter(userIdentity.getRemoteId(), userIdentityId); @@ -105,6 +111,9 @@ public List getOwnedProgramIds(String username, int offset, int limit) { @Override public List getMemberProgramIds(String username, int offset, int limit) { + if (StringUtils.isBlank(username)) { + return Collections.emptyList(); + } ProgramFilter programFilter = computeMemberProgramsFilter(username); return getProgramIds(programFilter, offset, limit); } @@ -130,6 +139,9 @@ public int countPrograms(ProgramFilter programFilter) { @Override public int countOwnedPrograms(String username) { + if (StringUtils.isBlank(username)) { + return 0; + } org.exoplatform.social.core.identity.model.Identity userIdentity = identityManager.getOrCreateUserIdentity(username); long userIdentityId = Long.parseLong(userIdentity.getId()); ProgramFilter programFilter = computeOwnedProgramsFilter(userIdentity.getRemoteId(), userIdentityId); @@ -138,6 +150,9 @@ public int countOwnedPrograms(String username) { @Override public int countMemberPrograms(String username) { + if (StringUtils.isBlank(username)) { + return 0; + } ProgramFilter programFilter = computeMemberProgramsFilter(username); return countPrograms(programFilter); } @@ -173,7 +188,7 @@ public ProgramDTO updateProgram(ProgramDTO program, Identity aclIdentity) throws ObjectNotFoundException { ProgramDTO storedProgram = programStorage.getProgramById(program.getId()); if (storedProgram == null) { - throw new ObjectNotFoundException("Program doesn't exist"); + throw new ObjectNotFoundException(PROGRAM_DOESN_T_EXIST); } if (storedProgram.isDeleted()) { throw new ObjectNotFoundException("Program is marked as deleted"); @@ -204,7 +219,7 @@ public ProgramDTO updateProgram(ProgramDTO program, Identity aclIdentity) throws public ProgramDTO updateProgram(ProgramDTO program) throws ObjectNotFoundException { ProgramDTO storedProgram = programStorage.getProgramById(program.getId()); if (storedProgram == null) { - throw new ObjectNotFoundException("Program doesn't exist"); + throw new ObjectNotFoundException(PROGRAM_DOESN_T_EXIST); } if (storedProgram.isDeleted()) { throw new ObjectNotFoundException("Program is marked as deleted"); @@ -225,12 +240,13 @@ public ProgramDTO deleteProgramById(long programId, Identity aclIdentity) throws ObjectNotFoundException { ProgramDTO program = programStorage.getProgramById(programId); if (program == null) { - throw new ObjectNotFoundException("program doesn't exist"); + throw new ObjectNotFoundException(PROGRAM_DOESN_T_EXIST); } if (aclIdentity == null || !isProgramOwner(programId, aclIdentity.getUserId())) { throw new IllegalAccessException("The user is not authorized to delete the program"); } program.setDeleted(true); + program.setVisibility(EntityVisibility.RESTRICTED); program = programStorage.saveProgram(program); broadcast(GAMIFICATION_DOMAIN_DELETE_LISTENER, program, aclIdentity.getUserId()); return program; @@ -241,7 +257,7 @@ public void deleteProgramCoverById(long programId, Identity aclIdentity) throws IllegalAccessException { ProgramDTO program = programStorage.getProgramById(programId); if (program == null) { - throw new ObjectNotFoundException("program doesn't exist"); + throw new ObjectNotFoundException(PROGRAM_DOESN_T_EXIST); } if (aclIdentity == null || !isProgramOwner(programId, aclIdentity.getUserId())) { throw new IllegalAccessException("The user is not authorized to delete the program cover"); @@ -261,7 +277,7 @@ public void deleteProgramAvatarById(long programId, Identity aclIdentity) throws IllegalAccessException { ProgramDTO program = programStorage.getProgramById(programId); if (program == null) { - throw new ObjectNotFoundException("program doesn't exist"); + throw new ObjectNotFoundException(PROGRAM_DOESN_T_EXIST); } if (aclIdentity == null || !isProgramOwner(programId, aclIdentity.getUserId())) { throw new IllegalAccessException("The user is not authorized to delete the program avatar"); @@ -278,18 +294,14 @@ public void deleteProgramAvatarById(long programId, Identity aclIdentity) throws @Override public ProgramDTO getProgramById(long programId, String username) throws IllegalAccessException, ObjectNotFoundException { - if (StringUtils.isBlank(username)) { - throw new IllegalAccessException("Username is mandatory"); - } ProgramDTO program = getProgramById(programId); if (program == null) { - throw new ObjectNotFoundException("Program doesn't exist"); + throw new ObjectNotFoundException(PROGRAM_DOESN_T_EXIST); } if (program.isDeleted()) { throw new ObjectNotFoundException("Program has been deleted"); } - if (!isProgramMember(programId, username) - || (!program.isEnabled() && !isProgramOwner(program.getId(), username))) { + if (!canViewProgram(program, username)) { throw new IllegalAccessException("Program isn't accessible"); } return program; @@ -307,10 +319,10 @@ public ProgramDTO getProgramById(long programId) { public InputStream getProgramCoverStream(long programId) throws ObjectNotFoundException { ProgramDTO program = programStorage.getProgramById(programId); if (program == null) { - throw new ObjectNotFoundException("program with id " + programId + " doesn't exist"); + throw new ObjectNotFoundException(String.format("program with id %s doesn't exist", programId)); } if (program.getCoverFileId() == 0) { - throw new ObjectNotFoundException("program with id " + programId + " doesn't have a coverId"); + throw new ObjectNotFoundException(String.format("program with id %s doesn't have a coverId", programId)); } return programStorage.getImageAsStream(program.getCoverFileId()); } @@ -374,6 +386,18 @@ public boolean canUseProgramColor(long programId, String color) { return canUseProgramColor(color, program == null ? null : program.getColor()); } + @Override + public boolean canViewProgram(long programId, String username) { + ProgramDTO program = getProgramById(programId); + return canViewProgram(program, username); + } + + private boolean canViewProgram(ProgramDTO program, String username) { + return program != null + && (program.getVisibility() == EntityVisibility.OPEN || isProgramMember(program.getId(), username)) + && (program.isEnabled() || isProgramOwner(program.getId(), username)); + } + @SuppressWarnings("unchecked") private ProgramFilter computeUserSpaces(ProgramFilter programFilter, String username) throws IllegalAccessException { // NOSONAR programFilter = programFilter.clone(); @@ -397,7 +421,7 @@ private ProgramFilter computeUserSpaces(ProgramFilter programFilter, String user } programFilter.setSpacesIds(managedSpaceIds); } - } else { + } else if (StringUtils.isNotBlank(username)) { List memberSpacesIds = spaceService.getMemberSpacesIds(username, 0, -1).stream().map(Long::parseLong).toList(); if (CollectionUtils.isNotEmpty(programFilter.getSpacesIds())) { memberSpacesIds = (List) CollectionUtils.intersection(memberSpacesIds, programFilter.getSpacesIds()); @@ -428,6 +452,7 @@ private ProgramDTO createProgram(ProgramDTO program, String username) { if (program.isOpen()) { program.setSpaceId(0); } + program.setVisibility(isSpaceOpen(program.getSpaceId()) ? EntityVisibility.OPEN : EntityVisibility.RESTRICTED); checkProgramColorUnicity(program.getColor(), null); return programStorage.saveProgram(program); } @@ -468,8 +493,11 @@ private boolean isSpaceManager(long spaceId, String username) { } return spaceService.isManager(space, username); } - + private boolean isSpaceMember(long spaceId, String username) { + if (StringUtils.isBlank(username)) { + return false; + } Space space = spaceService.getSpaceById(String.valueOf(spaceId)); if (space == null) { return false; @@ -477,6 +505,11 @@ private boolean isSpaceMember(long spaceId, String username) { return spaceService.isMember(space, username); } + private boolean isSpaceOpen(long spaceId) { + Space space = spaceId > 0 ? spaceService.getSpaceById(String.valueOf(spaceId)) : null; + return space == null || Space.OPEN.equals(space.getRegistration()); + } + private ProgramFilter computeOwnedProgramsFilter(String username, long userIdentityId) { ProgramFilter programFilter = new ProgramFilter(); programFilter.setIncludeDeleted(true); @@ -512,6 +545,7 @@ private ProgramFilter computeMemberProgramsFilter(String username) { private ProgramDTO saveProgramAndBroadcast(ProgramDTO program, ProgramDTO storedProgram, String username) { checkProgramColorUnicity(program.getColor(), storedProgram.getColor()); + program.setVisibility(isSpaceOpen(program.getSpaceId()) ? EntityVisibility.OPEN : EntityVisibility.RESTRICTED); program = programStorage.saveProgram(program); if (storedProgram.isEnabled() && !program.isEnabled()) { broadcast(GAMIFICATION_DOMAIN_DISABLE_LISTENER, program, username); diff --git a/services/src/main/java/io/meeds/gamification/service/impl/RealizationServiceImpl.java b/services/src/main/java/io/meeds/gamification/service/impl/RealizationServiceImpl.java index 0a4dea1ec0..9f178df380 100644 --- a/services/src/main/java/io/meeds/gamification/service/impl/RealizationServiceImpl.java +++ b/services/src/main/java/io/meeds/gamification/service/impl/RealizationServiceImpl.java @@ -525,7 +525,7 @@ public RealizationDTO getRealizationById(long realizationId, Identity userAclIde RealizationDTO realization = realizationStorage.getRealizationById(realizationId); if (realization == null) { throw new ObjectNotFoundException(String.format(REALIZATION_NOT_EXIST_MESSAGE, realizationId)); - } else if (programService.isProgramMember(realization.getProgram().getId(), userAclIdentity.getUserId()) + } else if (programService.canViewProgram(realization.getProgram().getId(), userAclIdentity.getUserId()) || realization.getEarnerId().equals(userIdentity.getId())) { return realization; } else { @@ -583,7 +583,7 @@ private RealizationFilter computeProgramFilter(RealizationFilter realizationFilt throw new IllegalArgumentException("filter is mandatory"); } if (userAclIdentity == null) { - throw new IllegalArgumentException("identity is mandatory"); + return null; } realizationFilter = realizationFilter.clone(); checkDates(realizationFilter.getFromDate(), realizationFilter.getToDate()); @@ -607,7 +607,7 @@ private RealizationFilter computeProgramFilter(RealizationFilter realizationFilt realizationFilter.setProgramIds(ownedProgramIds); } } - } else if (isFilterByPrograms && !isProgramsMember(filterProgramIds, userAclIdentity.getUserId())) { + } else if (isFilterByPrograms && !canViewPrograms(filterProgramIds, userAclIdentity.getUserId())) { throw new IllegalAccessException("User is not member of one or several selected programs :" + filterProgramIds); } else if (!isFilterByPrograms && !isSelfFilter(realizationFilter, username)) { List memberProgramIds = programService.getMemberProgramIds(userAclIdentity.getUserId(), 0, -1); @@ -656,9 +656,9 @@ private boolean isProgramsOwner(List programIds, String username) { .allMatch(programId -> programService.isProgramOwner(programId, username)); } - private boolean isProgramsMember(List programIds, String username) { + private boolean canViewPrograms(List programIds, String username) { return programIds.stream() - .allMatch(programId -> programService.isProgramMember(programId, username)); + .allMatch(programId -> programService.canViewProgram(programId, username)); } private List filterAuthorizedSpaces(List result, diff --git a/services/src/main/java/io/meeds/gamification/service/impl/RuleServiceImpl.java b/services/src/main/java/io/meeds/gamification/service/impl/RuleServiceImpl.java index ebebd5629f..a392818ae3 100644 --- a/services/src/main/java/io/meeds/gamification/service/impl/RuleServiceImpl.java +++ b/services/src/main/java/io/meeds/gamification/service/impl/RuleServiceImpl.java @@ -122,9 +122,6 @@ public RuleDTO findRuleById(long ruleId, String username) throws IllegalAccessEx if (ruleId <= 0) { throw new IllegalArgumentException("ruleId is mandatory"); } - if (StringUtils.isBlank(username)) { - throw new IllegalAccessException(USERNAME_IS_MANDATORY_MESSAGE); - } RuleDTO rule = findRuleById(ruleId); if (rule == null) { throw new ObjectNotFoundException("Rule doesn't exist"); @@ -135,7 +132,7 @@ public RuleDTO findRuleById(long ruleId, String username) throws IllegalAccessEx if (!isRuleManager(rule, username) && (!rule.isEnabled() || rule.getProgram() == null - || !programService.isProgramMember(rule.getProgram().getId(), username))) { + || !programService.canViewProgram(rule.getProgram().getId(), username))) { throw new IllegalAccessException("Rule isn't accessible"); } if (rule.getProgram() != null) { @@ -438,7 +435,7 @@ private RuleDTO createRuleAndBroadcast(RuleDTO rule, String username) { private boolean isRuleManager(RuleDTO rule, String username) { ProgramDTO program = rule.getProgram(); - if (program == null) { + if (program == null || StringUtils.isBlank(username)) { return false; } else { return programService.isProgramOwner(program.getId(), username); diff --git a/services/src/main/java/io/meeds/gamification/storage/mapper/ProgramMapper.java b/services/src/main/java/io/meeds/gamification/storage/mapper/ProgramMapper.java index bd87b8f822..05ee29274d 100644 --- a/services/src/main/java/io/meeds/gamification/storage/mapper/ProgramMapper.java +++ b/services/src/main/java/io/meeds/gamification/storage/mapper/ProgramMapper.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils; import io.meeds.gamification.constant.EntityType; +import io.meeds.gamification.constant.EntityVisibility; import io.meeds.gamification.dao.RuleDAO; import io.meeds.gamification.entity.ProgramEntity; import io.meeds.gamification.model.ProgramDTO; @@ -58,8 +59,10 @@ public static ProgramEntity toEntity(ProgramDTO program) { programEntity.setEnabled(program.isEnabled()); if (program.getSpaceId() > 0 && !program.isOpen()) { programEntity.setAudienceId(program.getSpaceId()); + programEntity.setVisibility(program.getVisibility()); } else { programEntity.setAudienceId(null); + programEntity.setVisibility(EntityVisibility.OPEN); } if (program.getCreatedDate() != null) { programEntity.setCreatedDate(Utils.parseRFC3339Date(program.getCreatedDate())); @@ -119,6 +122,10 @@ public static ProgramDTO fromEntity(RuleDAO ruleDAO, ProgramEntity programEntity program.setAvatarUrl(avatarUrl); program.setOwnerIds(programEntity.getOwners()); program.setOpen(programEntity.getAudienceId() == null); + program.setVisibility(programEntity.getVisibility()); + if (program.getVisibility() == null) { + program.setVisibility(EntityVisibility.RESTRICTED); + } program.setRulesTotalScore(programEntity.isDeleted() || !programEntity.isEnabled() ? 0 : ruleDAO.getRulesTotalScoreByProgramId(programEntity.getId())); return program; diff --git a/services/src/main/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePlugin.java b/services/src/main/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePlugin.java new file mode 100644 index 0000000000..392a8fc618 --- /dev/null +++ b/services/src/main/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePlugin.java @@ -0,0 +1,121 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * 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 + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.gamification.upgrade; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.TypedQuery; + +import org.exoplatform.commons.api.persistence.ExoTransactional; +import org.exoplatform.commons.api.settings.SettingService; +import org.exoplatform.commons.persistence.impl.EntityManagerService; +import org.exoplatform.commons.upgrade.UpgradeProductPlugin; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; + +import io.meeds.gamification.constant.EntityVisibility; + +public class ProgramVisibilityUpgradePlugin extends UpgradeProductPlugin { + + private static final Log LOG = ExoLogger.getLogger(ProgramVisibilityUpgradePlugin.class); + + private static final String VISIBILITY_PARAM = "visibility"; + + private static final String AUDIENCE_IDS_PARAM = "audienceIds"; + + private static final String GET_PROGRAM_SPACE_IDS_QUERY = + "SELECT DISTINCT p.audienceId FROM GamificationDomain p WHERE p.audienceId > 0"; + + private static final String UPGRADE_PROGRAMS_QUERY = + "UPDATE GamificationDomain p SET p.visibility = :" + VISIBILITY_PARAM; + + private static final String UPGRADE_PROGRAMS_WITH_AUDIENCE_QUERY = + UPGRADE_PROGRAMS_QUERY + + " WHERE p.audienceId IS NULL OR p.audienceId = 0 OR p.audienceId IN (:" + + AUDIENCE_IDS_PARAM + ")"; + + private static final String UPGRADE_PROGRAMS_NO_AUDIENCE_QUERY = + "UPDATE GamificationDomain p SET p.visibility = :" + + VISIBILITY_PARAM + + " WHERE p.audienceId IS NULL OR p.audienceId = 0"; + + private EntityManagerService entityManagerService; + + private SpaceService spaceService; + + public ProgramVisibilityUpgradePlugin(EntityManagerService entityManagerService, + SpaceService spaceService, + SettingService settingService, + InitParams initParams) { + super(settingService, initParams); + this.entityManagerService = entityManagerService; + this.spaceService = spaceService; + } + + @Override + public void processUpgrade(String oldVersion, String newVersion) { + LOG.info("Start:: Upgrade Programs Visibility"); + List spaceIds = getProgramSpaceIds(); + spaceIds = spaceIds.stream() + .map(spaceId -> spaceService.getSpaceById(String.valueOf(spaceId))) + .filter(Objects::nonNull) + .filter(s -> Space.OPEN.equals(s.getRegistration())) + .map(Space::getId) + .map(Long::parseLong) + .toList(); + upgradeAllProgramsAsRestricted(); + int openProgramsCount = upgradeProgramsAsOpen(spaceIds); + LOG.info("End:: Upgrade Programs Visibility: {} upgraded as Open.", + openProgramsCount); + } + + @ExoTransactional + public int upgradeAllProgramsAsRestricted() { + EntityManager entityManager = entityManagerService.getEntityManager(); + Query query = entityManager.createQuery(UPGRADE_PROGRAMS_QUERY); + query.setParameter(VISIBILITY_PARAM, EntityVisibility.RESTRICTED); + return query.executeUpdate(); + } + + @ExoTransactional + public int upgradeProgramsAsOpen(List programSpaceIds) { + EntityManager entityManager = entityManagerService.getEntityManager(); + Query query = entityManager.createQuery(programSpaceIds.isEmpty() ? UPGRADE_PROGRAMS_NO_AUDIENCE_QUERY : + UPGRADE_PROGRAMS_WITH_AUDIENCE_QUERY); + query.setParameter(VISIBILITY_PARAM, EntityVisibility.OPEN); + if (!programSpaceIds.isEmpty()) { + query.setParameter(AUDIENCE_IDS_PARAM, programSpaceIds); + } + return query.executeUpdate(); + } + + @ExoTransactional + public List getProgramSpaceIds() { + EntityManager entityManager = entityManagerService.getEntityManager(); + TypedQuery query = entityManager.createQuery(GET_PROGRAM_SPACE_IDS_QUERY, Long.class); + List result = query.getResultList(); + return result == null ? Collections.emptyList() : result; + } +} diff --git a/services/src/main/java/io/meeds/gamification/utils/Utils.java b/services/src/main/java/io/meeds/gamification/utils/Utils.java index 0fbfee8dbf..ea0ff63670 100644 --- a/services/src/main/java/io/meeds/gamification/utils/Utils.java +++ b/services/src/main/java/io/meeds/gamification/utils/Utils.java @@ -29,6 +29,7 @@ import org.exoplatform.services.log.Log; import org.exoplatform.services.security.Authenticator; import org.exoplatform.services.security.ConversationState; +import org.exoplatform.services.security.IdentityConstants; import org.exoplatform.services.security.IdentityRegistry; import org.exoplatform.social.core.identity.model.Identity; import org.exoplatform.social.core.manager.IdentityManager; @@ -42,6 +43,9 @@ import io.meeds.gamification.model.Announcement; import io.meeds.gamification.model.ProgramDTO; import io.meeds.gamification.model.RuleDTO; +import io.meeds.portal.security.constant.UserRegistrationType; +import io.meeds.portal.security.service.SecuritySettingService; + import org.exoplatform.ws.frameworks.json.JsonGenerator; import org.exoplatform.ws.frameworks.json.impl.*; @@ -262,14 +266,23 @@ public static long getUserIdentityId(String username) { Identity identity = identityManager.getOrCreateUserIdentity(username); return identity == null ? 0l : Long.parseLong(identity.getId()); } - + public static final String getCurrentUser() { if (ConversationState.getCurrent() != null && ConversationState.getCurrent().getIdentity() != null) { - return ConversationState.getCurrent().getIdentity().getUserId(); + String userId = ConversationState.getCurrent().getIdentity().getUserId(); + return StringUtils.equals(userId, IdentityConstants.ANONIM) ? null : userId; } return null; } + public static final boolean canAccessAnonymousResources() { + return canAccessAnonymousResources(ExoContainerContext.getService(SecuritySettingService.class)); + } + + public static final boolean canAccessAnonymousResources(SecuritySettingService securitySettingService) { + return StringUtils.isNotBlank(getCurrentUser()) || securitySettingService.getRegistrationType() == UserRegistrationType.OPEN; + } + public static String toRFC3339Date(Date dateTime) { if (dateTime == null) { return null; @@ -415,6 +428,9 @@ public static boolean isRewardingManager(String username) { } public static org.exoplatform.services.security.Identity getUserAclIdentity(String username) { + if (StringUtils.isBlank(username)) { + return null; + } IdentityRegistry identityRegistry = ExoContainerContext.getService(IdentityRegistry.class); org.exoplatform.services.security.Identity aclIdentity = identityRegistry.getIdentity(username); if (aclIdentity == null) { diff --git a/services/src/main/resources/db/changelog/gamification.db.changelog-1.0.0.xml b/services/src/main/resources/db/changelog/gamification.db.changelog-1.0.0.xml index 39f7e43ecb..d4b1cd01a2 100644 --- a/services/src/main/resources/db/changelog/gamification.db.changelog-1.0.0.xml +++ b/services/src/main/resources/db/changelog/gamification.db.changelog-1.0.0.xml @@ -751,4 +751,18 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + + + + + + + + diff --git a/services/src/test/java/io/meeds/gamification/listener/GamificationSpaceListenerTest.java b/services/src/test/java/io/meeds/gamification/listener/GamificationSpaceListenerTest.java index 1c3402423b..394c2a385d 100644 --- a/services/src/test/java/io/meeds/gamification/listener/GamificationSpaceListenerTest.java +++ b/services/src/test/java/io/meeds/gamification/listener/GamificationSpaceListenerTest.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,88 +38,108 @@ import java.util.Map; import java.util.function.BiConsumer; +import org.junit.AfterClass; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.junit.MockitoJUnitRunner; +import org.exoplatform.commons.utils.CommonsUtils; import org.exoplatform.services.listener.ListenerService; +import org.exoplatform.services.security.ConversationState; import org.exoplatform.social.core.identity.model.Identity; import org.exoplatform.social.core.manager.IdentityManager; import org.exoplatform.social.core.space.model.Space; import org.exoplatform.social.core.space.spi.SpaceLifeCycleEvent; +import org.exoplatform.social.core.space.spi.SpaceLifeCycleListener; import org.exoplatform.social.core.space.spi.SpaceService; import io.meeds.gamification.service.RuleService; @RunWith(MockitoJUnitRunner.Silent.class) -public class GamificationSpaceListenerTest { +public class GamificationSpaceListenerTest { // NOSONAR + + private static final MockedStatic COMMONS_UTILS_UTIL = mockStatic(CommonsUtils.class); @Mock - private RuleService ruleService; + private RuleService ruleService; @Mock - private IdentityManager identityManager; + private IdentityManager identityManager; @Mock - private SpaceService spaceService; + private SpaceService spaceService; @Mock - private ListenerService listenerService; + private ListenerService listenerService; + + @Before + public void setUp() { + COMMONS_UTILS_UTIL.when(() -> CommonsUtils.getService(IdentityManager.class)).thenReturn(identityManager); + ConversationState state = new ConversationState(new org.exoplatform.services.security.Identity("root")); + ConversationState.setCurrent(state); + } + + @AfterClass + public static void tearDown() { + COMMONS_UTILS_UTIL.close(); + } @Test public void testSpaceJoin() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_JOIN, (listener, event) -> listener.joined(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_JOIN, SpaceLifeCycleListener::joined); } @Test public void testSpaceLeave() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_JOIN, (listener, event) -> listener.left(event), true); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_JOIN, SpaceLifeCycleListener::left, true); } @Test public void testAddAppLicationSpace() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_APPLICATIONS, (listener, event) -> listener.applicationAdded(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_APPLICATIONS, SpaceLifeCycleListener::applicationAdded); } @Test public void testRemoveAppLicationSpace() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_APPLICATIONS, (listener, event) -> listener.applicationRemoved(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_APPLICATIONS, SpaceLifeCycleListener::applicationRemoved); } @Test public void testSpaceBannerEdited() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_BANNER, (listener, event) -> listener.spaceBannerEdited(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_BANNER, SpaceLifeCycleListener::spaceBannerEdited); } @Test public void testSpaceAvatarEdited() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_AVATAR, (listener, event) -> listener.spaceAvatarEdited(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_AVATAR, SpaceLifeCycleListener::spaceAvatarEdited); } @Test public void testCreateSpace() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_ADD, (listener, event) -> listener.spaceCreated(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_ADD, SpaceLifeCycleListener::spaceCreated); } @Test public void testUpdateSpaceDescription() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_DESCRIPTION, (listener, event) -> listener.spaceDescriptionEdited(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_UPDATE_DESCRIPTION, SpaceLifeCycleListener::spaceDescriptionEdited); } @Test public void testBecomeSpaceManager() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_GRANT_AS_LEAD, (listener, event) -> listener.grantedLead(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_GRANT_AS_LEAD, SpaceLifeCycleListener::grantedLead); } @Test public void testAddInvitedUser() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_INVITE_USER, (listener, event) -> listener.addInvitedUser(event)); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_INVITE_USER, SpaceLifeCycleListener::addInvitedUser); } @Test public void testRemoveInvitedUser() throws Exception { - testEventTrigger(GAMIFICATION_SOCIAL_SPACE_INVITE_USER, (listener, event) -> listener.removeInvitedUser(event), true); + testEventTrigger(GAMIFICATION_SOCIAL_SPACE_INVITE_USER, SpaceLifeCycleListener::removeInvitedUser, true); } private void testEventTrigger(String expectedGamifiedEvent, diff --git a/services/src/test/java/io/meeds/gamification/mock/SpaceServiceMock.java b/services/src/test/java/io/meeds/gamification/mock/SpaceServiceMock.java index ffc960940e..ff81168f14 100644 --- a/services/src/test/java/io/meeds/gamification/mock/SpaceServiceMock.java +++ b/services/src/test/java/io/meeds/gamification/mock/SpaceServiceMock.java @@ -45,7 +45,9 @@ public class SpaceServiceMock implements SpaceService { public static final String SPACE_DISPLAY_NAME = "test space"; - public static final String SPACE_ID = "1"; + public static final String SPACE_ID_1 = "1"; + + public static final String SPACE_ID_2 = "200"; public static final List SPACE_MEMBERS = Arrays.asList(new String[] { "root", @@ -85,10 +87,10 @@ public Space getSpaceByGroupId(String groupId) { } public Space getSpaceById(String spaceId) { - if (!SPACE_ID.equals(spaceId)) { + if (!SPACE_ID_1.equals(spaceId) && !SPACE_ID_2.equals(spaceId)) { return null; } - return getSpace(); + return getSpace(spaceId); } public boolean isRedactor(Space space, String userId) { @@ -134,7 +136,7 @@ public ListAccess getMemberSpaces(String username) { public List getMemberSpacesIds(String username, int offset, int limit) { if (SPACE_MEMBERS.contains(username)) { - return Collections.singletonList(SPACE_ID); + return Collections.singletonList(SPACE_ID_1); } else { return Collections.emptyList(); } @@ -142,7 +144,7 @@ public List getMemberSpacesIds(String username, int offset, int limit) { public List getManagerSpacesIds(String username, int offset, int limit) { if (SPACE_MANAGERS.contains(username)) { - return Collections.singletonList(SPACE_ID); + return Collections.singletonList(SPACE_ID_1); } else { return Collections.emptyList(); } @@ -673,8 +675,12 @@ public void removeSuperManagersMembership(String permissionExpression) { } private Space getSpace() { + return getSpace(SPACE_ID_1); + } + + private Space getSpace(String spaceId) { Space space = new Space(); - space.setId(SPACE_ID); + space.setId(spaceId); space.setPrettyName(SPACE_PRETTY_NAME); space.setDisplayName(SPACE_DISPLAY_NAME); space.setGroupId("/spaces/" + SPACE_PRETTY_NAME); @@ -682,6 +688,7 @@ private Space getSpace() { "root1", }); space.setMembers(SPACE_MEMBERS.toArray(new String[0])); + space.setRegistration(SPACE_ID_1.equals(spaceId) ? Space.VALIDATION : Space.OPEN); return space; } diff --git a/services/src/test/java/io/meeds/gamification/plugin/RuleActivityTypePluginTest.java b/services/src/test/java/io/meeds/gamification/plugin/RuleActivityTypePluginTest.java index 8331d4831b..f50072f242 100644 --- a/services/src/test/java/io/meeds/gamification/plugin/RuleActivityTypePluginTest.java +++ b/services/src/test/java/io/meeds/gamification/plugin/RuleActivityTypePluginTest.java @@ -116,11 +116,11 @@ public void testActivityTypePlugin() { activityManager.addActivityTypePlugin(new RuleActivityTypePlugin(programService, ruleService, initParams)); - when(programService.isProgramMember(rule.getProgramId(), owner.getUserId())).thenReturn(true); + when(programService.canViewProgram(rule.getProgramId(), owner.getUserId())).thenReturn(true); assertTrue(activityManager.isActivityViewable(activity, owner)); assertFalse(activityManager.isActivityViewable(activity, viewer)); - when(programService.isProgramMember(rule.getProgramId(), viewer.getUserId())).thenReturn(true); + when(programService.canViewProgram(rule.getProgramId(), viewer.getUserId())).thenReturn(true); assertTrue(activityManager.isActivityViewable(activity, viewer)); } diff --git a/services/src/test/java/io/meeds/gamification/rest/TestProgramRest.java b/services/src/test/java/io/meeds/gamification/rest/TestProgramRest.java index 8a2dff5daa..41a7c6debe 100644 --- a/services/src/test/java/io/meeds/gamification/rest/TestProgramRest.java +++ b/services/src/test/java/io/meeds/gamification/rest/TestProgramRest.java @@ -27,10 +27,15 @@ import org.exoplatform.services.rest.impl.ContainerResponse; import org.exoplatform.services.security.ConversationState; +import io.meeds.gamification.constant.EntityFilterType; +import io.meeds.gamification.constant.EntityStatusType; import io.meeds.gamification.constant.EntityType; import io.meeds.gamification.entity.ProgramEntity; +import io.meeds.gamification.mock.SpaceServiceMock; import io.meeds.gamification.model.ProgramDTO; import io.meeds.gamification.model.UserInfoContext; +import io.meeds.gamification.model.filter.ProgramFilter; +import io.meeds.gamification.rest.model.ProgramList; import io.meeds.gamification.rest.model.ProgramRestEntity; import io.meeds.gamification.test.AbstractServiceTest; import io.meeds.gamification.utils.Utils; @@ -173,8 +178,8 @@ public void testGetProgramCoverById() throws Exception { StandardCharsets.UTF_8); ContainerResponse response = getResponse("GET", - getURLResource("programs/" + Utils.DEFAULT_COVER_REMOTE_ID + "/cover?lastModified=" - + lastUpdateCoverTime + "&r=" + token), + getURLResource("programs/" + Utils.DEFAULT_COVER_REMOTE_ID + "/cover?lastModified=" + + lastUpdateCoverTime + "&r=" + token), null); assertNotNull(response); assertEquals(200, response.getStatus()); @@ -184,17 +189,15 @@ public void testGetProgramCoverById() throws Exception { assertEquals(404, response.getStatus()); response = getResponse("GET", - getURLResource("programs/" + manualDomain.getId() + "/cover?lastModified=" + lastUpdateCoverTime - + "&r=" - + token), + getURLResource("programs/" + manualDomain.getId() + "/cover?lastModified=" + lastUpdateCoverTime + + "&r=" + token), null); assertNotNull(response); assertEquals(200, response.getStatus()); response = getResponse("GET", - getURLResource("programs/" + manualDomain.getId() + "/cover?lastModified=" + lastUpdateCoverTime - + "&r=" - + "wrongToken"), + getURLResource("programs/" + manualDomain.getId() + "/cover?lastModified=" + lastUpdateCoverTime + + "&r=" + "wrongToken"), null); assertNotNull(response); assertEquals(403, response.getStatus()); @@ -209,8 +212,8 @@ public void testGetProgramAvatarById() throws Exception { StandardCharsets.UTF_8); ContainerResponse response = getResponse("GET", - getURLResource("programs/" + Utils.DEFAULT_AVATAR_REMOTE_ID + "/avatar?lastModified=" - + lastUpdateAvatarTime + "&r=" + token), + getURLResource("programs/" + Utils.DEFAULT_AVATAR_REMOTE_ID + + "/avatar?lastModified=" + lastUpdateAvatarTime + "&r=" + token), null); assertNotNull(response); assertEquals(200, response.getStatus()); @@ -221,17 +224,15 @@ public void testGetProgramAvatarById() throws Exception { assertEquals(404, response.getStatus()); response = getResponse("GET", - getURLResource("programs/" + manualDomain.getId() + "/avatar?lastModified=" + lastUpdateAvatarTime - + "&r=" - + token), + getURLResource("programs/" + manualDomain.getId() + "/avatar?lastModified=" + lastUpdateAvatarTime + + "&r=" + token), null); assertNotNull(response); assertEquals(200, response.getStatus()); response = getResponse("GET", - getURLResource("programs/" + manualDomain.getId() + "/avatar?lastModified=" + lastUpdateAvatarTime - + "&r=" - + "wrongToken"), + getURLResource("programs/" + manualDomain.getId() + "/avatar?lastModified=" + lastUpdateAvatarTime + + "&r=" + "wrongToken"), null); assertNotNull(response); assertEquals(403, response.getStatus()); @@ -305,4 +306,77 @@ public void testGetOpenProgramById() throws Exception { assertTrue(((UserInfoContext) savedProgram.getUserInfo()).isMember()); assertTrue(((UserInfoContext) savedProgram.getUserInfo()).isProgramOwner()); } + + @Test + public void testGetAccessibleProgramById() throws Exception { + ProgramFilter filter = new ProgramFilter(); + filter.setType(EntityFilterType.ALL); + filter.setStatus(EntityStatusType.ENABLED); + assertEquals(0, programService.getPrograms(filter, null, offset, 10).size()); + assertEquals(0, programService.countPrograms(filter, null)); + + ProgramEntity programEntity = newDomain(EntityType.AUTOMATIC, "testGetAccessibleProgramById", true, Collections.emptySet()); + ContainerResponse response = getResponse("GET", getURLResource("programs/" + programEntity.getId()), null); + assertNotNull(response); + assertEquals(401, response.getStatus()); + + ProgramDTO program = programService.getProgramById(programEntity.getId()); + program.setSpaceId(Long.parseLong(SpaceServiceMock.SPACE_ID_2)); + programService.updateProgram(program); + + response = getResponse("GET", getURLResource("programs/" + programEntity.getId()), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + ProgramRestEntity programRestEntity = (ProgramRestEntity) response.getEntity(); + assertNotNull(programRestEntity); + assertEquals(programEntity.getId().longValue(), programRestEntity.getId()); + assertTrue(((UserInfoContext) programRestEntity.getUserInfo()).isCanView()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isCanEdit()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isManager()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isMember()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isProgramOwner()); + } + + @Test + public void testGetAccessiblePrograms() throws Exception { + ProgramFilter filter = new ProgramFilter(); + filter.setType(EntityFilterType.ALL); + filter.setStatus(EntityStatusType.ENABLED); + assertEquals(0, programService.getPrograms(filter, null, offset, 10).size()); + assertEquals(0, programService.countPrograms(filter, null)); + + ProgramEntity programEntity = newDomain(EntityType.AUTOMATIC, "testGetAccessiblePrograms", true, Collections.emptySet()); + ContainerResponse response = getResponse("GET", getURLResource("programs?offset=0&limit=10&returnSize=true"), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + + ProgramList programList = (ProgramList) response.getEntity(); + assertNotNull(programList); + assertEquals(0, programList.getSize()); + assertEquals(0, programList.getPrograms().size()); + + ProgramDTO program = programService.getProgramById(programEntity.getId()); + program.setSpaceId(Long.parseLong(SpaceServiceMock.SPACE_ID_2)); + programService.updateProgram(program); + + response = getResponse("GET", getURLResource("programs?offset=0&limit=10&returnSize=true"), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + + programList = (ProgramList) response.getEntity(); + assertNotNull(programList); + assertNotNull(programList.getPrograms()); + assertEquals(1, programList.getPrograms().size()); + assertEquals(1, programList.getSize()); + + ProgramRestEntity programRestEntity = programList.getPrograms().get(0); + assertNotNull(programRestEntity); + assertEquals(programEntity.getId().longValue(), programRestEntity.getId()); + assertTrue(((UserInfoContext) programRestEntity.getUserInfo()).isCanView()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isCanEdit()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isManager()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isMember()); + assertFalse(((UserInfoContext) programRestEntity.getUserInfo()).isProgramOwner()); + } + } diff --git a/services/src/test/java/io/meeds/gamification/rest/TestRealizationRest.java b/services/src/test/java/io/meeds/gamification/rest/TestRealizationRest.java index fc743f5e5c..37092e482a 100644 --- a/services/src/test/java/io/meeds/gamification/rest/TestRealizationRest.java +++ b/services/src/test/java/io/meeds/gamification/rest/TestRealizationRest.java @@ -212,6 +212,14 @@ public void testGetAllRealizationsSortByDateAscending() throws Exception { List realizations = realizationList.getRealizations(); assertEquals(0, realizations.size()); + startSessionAs("root1"); + response = getResponse("GET", getURLResource(restPath), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + realizationList = (RealizationList) response.getEntity(); + realizations = realizationList.getRealizations(); + assertEquals(0, realizations.size()); + // add new realization List createdActionHistories = new ArrayList<>(); ProgramEntity domainEntity = newDomain(); diff --git a/services/src/test/java/io/meeds/gamification/rest/TestRuleRest.java b/services/src/test/java/io/meeds/gamification/rest/TestRuleRest.java index 990974ec1d..d43328378b 100644 --- a/services/src/test/java/io/meeds/gamification/rest/TestRuleRest.java +++ b/services/src/test/java/io/meeds/gamification/rest/TestRuleRest.java @@ -32,8 +32,11 @@ import io.meeds.gamification.constant.EntityType; import io.meeds.gamification.entity.ProgramEntity; import io.meeds.gamification.entity.RuleEntity; +import io.meeds.gamification.mock.SpaceServiceMock; import io.meeds.gamification.model.ProgramDTO; import io.meeds.gamification.model.RuleDTO; +import io.meeds.gamification.model.UserInfo; +import io.meeds.gamification.model.UserInfoContext; import io.meeds.gamification.rest.model.ProgramWithRulesRestEntity; import io.meeds.gamification.rest.model.RuleList; import io.meeds.gamification.rest.model.RuleRestEntity; @@ -263,7 +266,7 @@ public void testUpdateRule() throws Exception { assertNotNull(response); assertEquals(404, response.getStatus()); } - + @Test public void testGetRuleById() throws Exception { startSessionAs("root1"); @@ -271,40 +274,40 @@ public void testGetRuleById() throws Exception { StringWriter writer = new StringWriter(); JSONWriter jsonWriter = new JSONWriter(writer); jsonWriter.object() - .key("title") - .value("Rule") - .key("description") - .value("Rule description") - .key("startDate") - .value(START_DATE) - .key("endDate") - .value(END_DATE) - .key("points") - .value("10") - .key("program") - .object() - .key("id") - .value(domain.getId()) - .endObject() - .endObject(); - + .key("title") + .value("Rule") + .key("description") + .value("Rule description") + .key("startDate") + .value(START_DATE) + .key("endDate") + .value(END_DATE) + .key("points") + .value("10") + .key("program") + .object() + .key("id") + .value(domain.getId()) + .endObject() + .endObject(); + ContainerResponse response = getResponse("POST", getURLResource("rules"), writer.getBuffer().toString()); assertNotNull(response); assertEquals(200, response.getStatus()); RuleRestEntity ruleRestEntity = (RuleRestEntity) response.getEntity(); assertNotNull(ruleRestEntity); startSessionAs("root2"); - + response = getResponse("GET", getURLResource("rules/0"), null); assertNotNull(response); assertEquals(400, response.getStatus()); - + response = getResponse("GET", getURLResource("rules/555"), null); assertNotNull(response); assertEquals(404, response.getStatus()); - + startSessionAs("root1"); - + response = getResponse("GET", getURLResource("rules/" + ruleRestEntity.getId()), null); assertNotNull(response); assertEquals(200, response.getStatus()); @@ -313,43 +316,43 @@ public void testGetRuleById() throws Exception { assertEquals(ruleRestEntity.getId(), savedRuleRestEntity.getId()); assertFalse(savedRuleRestEntity.isPublished()); } - + @Test public void testGetRules() throws Exception { ProgramEntity domainEntity = newDomain(); - + newRule("rule", domainEntity.getId()); newRule("rule1", domainEntity.getId()); - + startSessionAs("root0"); ContainerResponse response = getResponse("GET", getURLResource("rules?returnSize=true"), null); assertEquals(200, response.getStatus()); - + response = getResponse("GET", getURLResource("rules?returnSize=true&limit=-1"), null); assertEquals(400, response.getStatus()); - + response = getResponse("GET", getURLResource("rules?returnSize=true&offset=-1"), null); assertEquals(400, response.getStatus()); - + response = getResponse("GET", getURLResource("rules?returnSize=true&programId=" + domainEntity.getId()), null); assertEquals(200, response.getStatus()); RuleList rules = (RuleList) response.getEntity(); assertNotNull(rules); assertEquals(0, rules.getSize()); - + startSessionAsAdministrator("root1"); response = getResponse("GET", getURLResource("rules?returnSize=true&programId=" + domainEntity.getId()), null); assertEquals(200, response.getStatus()); rules = (RuleList) response.getEntity(); assertNotNull(rules); assertEquals(2, rules.getSize()); - + response = getResponse("GET", getURLResource("rules?returnSize=true&spaceId=5555"), null); assertEquals(200, response.getStatus()); rules = (RuleList) response.getEntity(); assertNotNull(rules); assertEquals(0, rules.getSize()); - + response = getResponse("GET", getURLResource("rules?returnSize=true&spaceId=" + domainEntity.getAudienceId() + "&spaceId=5555"), null); @@ -358,7 +361,7 @@ public void testGetRules() throws Exception { assertNotNull(rules); assertEquals(2, rules.getSize()); } - + @Test public void testGetRulesByUser() throws Exception { startSessionAs("root1"); @@ -367,27 +370,27 @@ public void testGetRulesByUser() throws Exception { StringWriter writer = new StringWriter(); JSONWriter jsonWriter = new JSONWriter(writer); jsonWriter.object() - .key("title") - .value("Rule") - .key("description") - .value("Rule description") - .key("startDate") - .value(START_DATE) - .key("endDate") - .value(END_DATE) - .key("points") - .value("10") - .key("enabled") - .value(true) - .key("program") - .object() - .key("id") - .value(domain.getId()) - .endObject() - .endObject(); - + .key("title") + .value("Rule") + .key("description") + .value("Rule description") + .key("startDate") + .value(START_DATE) + .key("endDate") + .value(END_DATE) + .key("points") + .value("10") + .key("enabled") + .value(true) + .key("program") + .object() + .key("id") + .value(domain.getId()) + .endObject() + .endObject(); + ContainerResponse response = getResponse("POST", restPath, writer.getBuffer().toString()); - + assertNotNull(response); assertEquals(200, response.getStatus()); RuleRestEntity rule = (RuleRestEntity) response.getEntity(); @@ -398,23 +401,23 @@ public void testGetRulesByUser() throws Exception { rule = (RuleRestEntity) response.getEntity(); assertNotNull(rule); startSessionAs("root2"); - + restPath = GAMIFICATION_RULES_REST_PATH + "?offset=0&limit=10"; response = getResponse("GET", restPath, null); assertNotNull(response); assertEquals(200, response.getStatus()); RuleList savedRules = (RuleList) response.getEntity(); assertEquals(0, savedRules.getSize()); - + startSessionAs("root1"); - + restPath = GAMIFICATION_RULES_REST_PATH + "?offset=0&limit=10&programId=" + domain.getId() + "&announcements=4"; response = getResponse("GET", restPath, null); assertNotNull(response); assertEquals(200, response.getStatus()); savedRules = (RuleList) response.getEntity(); assertEquals(2, savedRules.getRules().size()); - + restPath = GAMIFICATION_RULES_REST_PATH + "?offset=0&limit=10&programId=0&announcements=4&groupByProgram=true"; response = getResponse("GET", restPath, null); assertNotNull(response); @@ -422,7 +425,7 @@ public void testGetRulesByUser() throws Exception { List domainWithRules = (List) response.getEntity(); assertEquals(1, domainWithRules.size()); assertEquals(2, domainWithRules.get(0).getRules().size()); - + restPath = GAMIFICATION_RULES_REST_PATH + "?offset=0&limit=1&returnSize=true"; response = getResponse("GET", restPath, null); assertNotNull(response); @@ -432,6 +435,83 @@ public void testGetRulesByUser() throws Exception { assertEquals(1, savedRules.getRules().size()); } + @Test + public void testGetRuleByAnonym() throws Exception { + ProgramDTO program = newProgram(); + program = programService.createProgram(program); + + RuleEntity rule = newRule("testGetRuleByAnonym", program.getId()); + + ContainerResponse response = getResponse("GET", getURLResource("rules/0"), null); + assertNotNull(response); + assertEquals(400, response.getStatus()); + + response = getResponse("GET", getURLResource("rules/555"), null); + assertNotNull(response); + assertEquals(404, response.getStatus()); + + response = getResponse("GET", getURLResource("rules/" + rule.getId()), null); + assertNotNull(response); + assertEquals(401, response.getStatus()); + + program.setSpaceId(Long.parseLong(SpaceServiceMock.SPACE_ID_2)); + programService.updateProgram(program); + + response = getResponse("GET", getURLResource("rules/" + rule.getId()), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + RuleRestEntity ruleRestEntity = (RuleRestEntity) response.getEntity(); + assertNotNull(ruleRestEntity); + assertEquals(rule.getId(), ruleRestEntity.getId()); + UserInfoContext userInfo = (UserInfoContext) ruleRestEntity.getUserInfo(); + assertTrue(userInfo.isCanView()); + assertFalse(userInfo.isCanEdit()); + assertFalse(userInfo.isManager()); + assertFalse(userInfo.isMember()); + assertFalse(userInfo.isProgramOwner()); + assertFalse(userInfo.isRedactor()); + } + + @Test + public void testGetRulesByAnonym() throws Exception { + ProgramDTO program = newProgram(); + program = programService.createProgram(program); + + RuleEntity rule = newRule("testGetRulesByAnonym", program.getId()); + + ContainerResponse response = getResponse("GET", getURLResource("rules?offset=0&limit=10&returnSize=true"), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + RuleList ruleList = (RuleList) response.getEntity(); + assertNotNull(ruleList); + assertEquals(0, ruleList.getSize()); + assertNotNull(ruleList.getRules()); + assertEquals(0, ruleList.getRules().size()); + + program.setSpaceId(Long.parseLong(SpaceServiceMock.SPACE_ID_2)); + programService.updateProgram(program); + + response = getResponse("GET", getURLResource("rules?offset=0&limit=10&returnSize=true"), null); + assertNotNull(response); + assertEquals(200, response.getStatus()); + ruleList = (RuleList) response.getEntity(); + assertNotNull(ruleList); + assertEquals(1, ruleList.getSize()); + assertNotNull(ruleList.getRules()); + assertEquals(1, ruleList.getRules().size()); + + RuleRestEntity ruleRestEntity = ruleList.getRules().get(0); + assertNotNull(ruleRestEntity); + assertEquals(rule.getId(), ruleRestEntity.getId()); + UserInfoContext userInfo = (UserInfoContext) ruleRestEntity.getUserInfo(); + assertTrue(userInfo.isCanView()); + assertFalse(userInfo.isCanEdit()); + assertFalse(userInfo.isManager()); + assertFalse(userInfo.isMember()); + assertFalse(userInfo.isProgramOwner()); + assertFalse(userInfo.isRedactor()); + } + @Test public void testCreateAndDeleteRule() throws Exception { startSessionAs("root1"); diff --git a/services/src/test/java/io/meeds/gamification/service/ProgramServiceTest.java b/services/src/test/java/io/meeds/gamification/service/ProgramServiceTest.java index 7f17ecab81..10fec99f35 100644 --- a/services/src/test/java/io/meeds/gamification/service/ProgramServiceTest.java +++ b/services/src/test/java/io/meeds/gamification/service/ProgramServiceTest.java @@ -38,6 +38,7 @@ import io.meeds.gamification.constant.EntityStatusType; import io.meeds.gamification.constant.EntityType; import io.meeds.gamification.entity.ProgramEntity; +import io.meeds.gamification.mock.SpaceServiceMock; import io.meeds.gamification.model.ProgramColorAlreadyExists; import io.meeds.gamification.model.ProgramDTO; import io.meeds.gamification.model.RuleDTO; @@ -214,14 +215,38 @@ public void testGetDomainsByOwner() throws IllegalAccessException { filter.setType(EntityFilterType.ALL); filter.setStatus(EntityStatusType.ENABLED); assertEquals(0, programService.getPrograms(filter, SPACE_MEMBER_USER, offset, 10).size()); - ProgramEntity domainEntity = newDomain(EntityType.AUTOMATIC, "domain10", true, Collections.emptySet()); + assertEquals(0, programService.countPrograms(filter, SPACE_MEMBER_USER)); + + ProgramEntity programEntity = newDomain(EntityType.AUTOMATIC, "domain10", true, Collections.emptySet()); filter.setOwnerId(10); assertEquals(0, programService.getPrograms(filter, SPACE_MEMBER_USER, offset, 10).size()); + assertEquals(0, programService.countPrograms(filter, SPACE_MEMBER_USER)); - domainEntity.setOwners(Collections.singleton(10l)); - programDAO.update(domainEntity); + programEntity.setOwners(Collections.singleton(10l)); + programDAO.update(programEntity); assertEquals(1, programService.getPrograms(filter, SPACE_MEMBER_USER, offset, 10).size()); + assertEquals(1, programService.countPrograms(filter, SPACE_MEMBER_USER)); + } + + @Test + public void testGetDomainsByAnonym() throws IllegalAccessException, ObjectNotFoundException { + ProgramFilter filter = new ProgramFilter(); + filter.setType(EntityFilterType.ALL); + filter.setStatus(EntityStatusType.ENABLED); + assertEquals(0, programService.getPrograms(filter, null, offset, 10).size()); + assertEquals(0, programService.countPrograms(filter, null)); + + ProgramEntity programEntity = newDomain(EntityType.AUTOMATIC, "domain10", true, Collections.emptySet()); + assertEquals(0, programService.getPrograms(filter, null, offset, 10).size()); + assertEquals(0, programService.countPrograms(filter, null)); + + ProgramDTO program = programService.getProgramById(programEntity.getId()); + program.setSpaceId(Long.parseLong(SpaceServiceMock.SPACE_ID_2)); + programService.updateProgram(program); + + assertEquals(1, programService.getPrograms(filter, null, offset, 10).size()); + assertEquals(1, programService.countPrograms(filter, null)); } @Test diff --git a/services/src/test/java/io/meeds/gamification/service/RealizationServiceMockTest.java b/services/src/test/java/io/meeds/gamification/service/RealizationServiceMockTest.java index 8d234ef53b..eb00b100ef 100644 --- a/services/src/test/java/io/meeds/gamification/service/RealizationServiceMockTest.java +++ b/services/src/test/java/io/meeds/gamification/service/RealizationServiceMockTest.java @@ -157,7 +157,8 @@ public void testGetRealizationsByFilter() throws IllegalAccessException { when(identityManager.getOrCreateUserIdentity(userAclIdentity.getUserId())).thenReturn(adminIdentity); assertThrows(IllegalArgumentException.class, () -> realizationService.getRealizationsByFilter(null, userAclIdentity, offset, limit)); - assertThrows(IllegalArgumentException.class, () -> realizationService.getRealizationsByFilter(filter, null, offset, limit)); + assertEquals(0, realizationService.getRealizationsByFilter(filter, null, offset, limit).size()); + assertEquals(0, realizationService.countRealizationsByFilter(filter, userAclIdentity)); // When filter.setFromDate(fromDate); @@ -201,7 +202,7 @@ public void testGetRealizationsByFilter() throws IllegalAccessException { filter.setFromDate(fromDate); filter.setToDate(toDate); assertThrows(IllegalArgumentException.class, () -> realizationService.countRealizationsByFilter(null, userAclIdentity)); - assertThrows(IllegalArgumentException.class, () -> realizationService.countRealizationsByFilter(filter, null)); + assertEquals(0, realizationService.countRealizationsByFilter(filter, null)); // When filter.setEarnerIds(null); diff --git a/services/src/test/java/io/meeds/gamification/test/AbstractServiceTest.java b/services/src/test/java/io/meeds/gamification/test/AbstractServiceTest.java index 784588e821..912c57f0da 100644 --- a/services/src/test/java/io/meeds/gamification/test/AbstractServiceTest.java +++ b/services/src/test/java/io/meeds/gamification/test/AbstractServiceTest.java @@ -261,6 +261,7 @@ public void setUp() throws Exception { resourceBinder.clear(); ApplicationContextImpl.setCurrent(new ApplicationContextImpl(null, null, providerBinder, null)); launcher = new ResourceLauncher(requestHandler); + ConversationState.setCurrent(null); begin(); } diff --git a/services/src/test/java/io/meeds/gamification/test/InitContainerTestSuite.java b/services/src/test/java/io/meeds/gamification/test/InitContainerTestSuite.java index e627f87272..191706e24f 100644 --- a/services/src/test/java/io/meeds/gamification/test/InitContainerTestSuite.java +++ b/services/src/test/java/io/meeds/gamification/test/InitContainerTestSuite.java @@ -76,6 +76,7 @@ import io.meeds.gamification.storage.ProgramStorageTest; import io.meeds.gamification.storage.RealizationsStorageTest; import io.meeds.gamification.storage.RuleStorageTest; +import io.meeds.gamification.upgrade.ProgramVisibilityUpgradePluginTest; import io.meeds.gamification.utils.UtilsTest; @RunWith(Suite.class) @@ -132,6 +133,7 @@ ActionPublishedNotificationPluginTest.class, RuleActivityTypePluginTest.class, EventServiceTest.class, + ProgramVisibilityUpgradePluginTest.class, }) @ConfigTestCase(AbstractServiceTest.class) public class InitContainerTestSuite extends BaseExoContainerTestSuite { diff --git a/services/src/test/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePluginTest.java b/services/src/test/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePluginTest.java new file mode 100644 index 0000000000..b8c4d25b6b --- /dev/null +++ b/services/src/test/java/io/meeds/gamification/upgrade/ProgramVisibilityUpgradePluginTest.java @@ -0,0 +1,71 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * 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 + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.gamification.upgrade; + +import org.exoplatform.commons.api.settings.SettingService; +import org.exoplatform.commons.persistence.impl.EntityManagerService; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ValueParam; +import org.exoplatform.social.core.space.spi.SpaceService; + +import io.meeds.gamification.constant.EntityVisibility; +import io.meeds.gamification.entity.ProgramEntity; +import io.meeds.gamification.mock.SpaceServiceMock; +import io.meeds.gamification.test.AbstractServiceTest; + +public class ProgramVisibilityUpgradePluginTest extends AbstractServiceTest {// NOSONAR + + public void testProgramUpgrade() { + + InitParams initParams = new InitParams(); + + ValueParam valueParam = new ValueParam(); + valueParam.setName("product.group.id"); + valueParam.setValue("org.exoplatform.social"); + initParams.addParam(valueParam); + + valueParam = new ValueParam(); + valueParam.setName("plugin.execution.order"); + valueParam.setValue("5"); + initParams.addParam(valueParam); + + ProgramEntity program1 = newDomain("testProgramUpgrade1"); + ProgramEntity program2 = newDomain("testProgramUpgrade2"); + ProgramEntity program3 = newDomain("testProgramUpgrade3"); + program3.setAudienceId(Long.parseLong(SpaceServiceMock.SPACE_ID_2)); + program3 = programDAO.update(program3); + + EntityManagerService entityManagerService = getContainer().getComponentInstanceOfType(EntityManagerService.class); + SettingService settingService = getContainer().getComponentInstanceOfType(SettingService.class); + SpaceService spaceService = getContainer().getComponentInstanceOfType(SpaceService.class); + ProgramVisibilityUpgradePlugin upgradePlugin = new ProgramVisibilityUpgradePlugin(entityManagerService, + spaceService, + settingService, + initParams); + upgradePlugin.setName("ProgramVisibilityUpgradePlugin"); + assertTrue(upgradePlugin.isEnabled()); + + upgradePlugin.processUpgrade(null, null); + restartTransaction(); + + assertEquals(EntityVisibility.RESTRICTED, programDAO.find(program1.getId()).getVisibility()); + assertEquals(EntityVisibility.RESTRICTED, programDAO.find(program2.getId()).getVisibility()); + assertEquals(EntityVisibility.OPEN, programDAO.find(program3.getId()).getVisibility()); + } + +}