diff --git a/Backend/alfresco/common/src/main/java/org/edu_sharing/repository/client/tools/CCConstants.java b/Backend/alfresco/common/src/main/java/org/edu_sharing/repository/client/tools/CCConstants.java index 1944159359..ea62ed8c4c 100644 --- a/Backend/alfresco/common/src/main/java/org/edu_sharing/repository/client/tools/CCConstants.java +++ b/Backend/alfresco/common/src/main/java/org/edu_sharing/repository/client/tools/CCConstants.java @@ -477,6 +477,8 @@ public class CCConstants { public final static String CCM_ASPECT_EDUGROUP = "{http://www.campuscontent.de/model/1.0}edugroup"; + public final static String CCM_ASPECT_EDUGROUP_FOLDER = "{http://www.campuscontent.de/model/1.0}edugroup_folder"; + public final static String CCM_ASPECT_MAP_REF = "{http://www.campuscontent.de/model/1.0}map_ref"; public final static String CCM_ASPECT_GROUPEXTENSION = "{http://www.campuscontent.de/model/1.0}groupExtension"; @@ -520,6 +522,8 @@ public class CCConstants { public final static String CCM_PROP_EDUGROUP_EDU_HOMEDIR = "{http://www.campuscontent.de/model/1.0}edu_homedir"; + public final static String CCM_PROP_EDUGROUP_FOLDER_ORGANISATION = "{http://www.campuscontent.de/model/1.0}edu_organisation"; + public final static String CCM_ASPECT_SHARES = "{http://www.campuscontent.de/model/1.0}shares"; public final static String CCM_ASPECT_WORKFLOW = "{http://www.campuscontent.de/model/1.0}workflow"; diff --git a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-cache-context.xml b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-cache-context.xml index 9042321dc9..5dd0f08136 100644 --- a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-cache-context.xml +++ b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-cache-context.xml @@ -13,13 +13,6 @@ - - - - - - - diff --git a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-core-services-context.xml b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-core-services-context.xml index 6e72c21883..b07d3208ed 100644 --- a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-core-services-context.xml +++ b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/custom-core-services-context.xml @@ -351,6 +351,9 @@ + + + @@ -385,24 +388,6 @@ - - - - - - - - - - - - - - - - - - @@ -771,6 +756,11 @@ + + + + + @@ -865,6 +855,6 @@ - + diff --git a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/es_models/ccmodel.xml b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/es_models/ccmodel.xml index 21433234b0..5826582b85 100644 --- a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/es_models/ccmodel.xml +++ b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/es_models/ccmodel.xml @@ -1780,6 +1780,18 @@ + + + EduGroupFolder - with ref to home organisation + + + home organisation of the folder + d:noderef + false + + + + diff --git a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/model/ccContentModel.xml b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/model/ccContentModel.xml index 11fd4d4dea..520000c539 100644 --- a/Backend/alfresco/module/src/main/amp/config/alfresco/extension/model/ccContentModel.xml +++ b/Backend/alfresco/module/src/main/amp/config/alfresco/extension/model/ccContentModel.xml @@ -199,7 +199,8 @@ true false - both + + false diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/fixes/VirtualEduGroupFolderTool.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/fixes/VirtualEduGroupFolderTool.java index b94c7a7a02..75ec62a4a9 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/fixes/VirtualEduGroupFolderTool.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/fixes/VirtualEduGroupFolderTool.java @@ -1,7 +1,6 @@ package org.edu_sharing.alfresco.fixes; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Set; @@ -14,12 +13,9 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.namespace.QName; import org.apache.log4j.Logger; -import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; import org.edu_sharing.repository.client.tools.CCConstants; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; public class VirtualEduGroupFolderTool { @@ -33,43 +29,6 @@ public VirtualEduGroupFolderTool(ServiceRegistry serviceRegistry, NodeService no this.nodeService = nodeService; } - /*public List getGroupMapChildren2(NodeRef nodeRef) { - if (QName.createQName(CCConstants.CCM_TYPE_MAP).equals(nodeService.getType(nodeRef))) { - String mapType = (String) nodeService.getProperty(nodeRef, QName.createQName(CCConstants.CCM_PROP_MAP_TYPE)); - if (mapType != null && mapType.equals(CCConstants.CCM_VALUE_MAP_TYPE_EDUGROUP)) { - logger.debug("its a map with typ edugroup"); - OwnableService ownableService = (OwnableService)AlfAppContextGate.getApplicationContext().getBean("OwnableService"); - String user = ownableService.getOwner(nodeRef); - - Set authorities = serviceRegistry.getAuthorityService().getContainingAuthorities(AuthorityType.GROUP, user, true); - - Collection eduGroupNodeRefs = getEduGroupNodeRefs(); - - List children = new ArrayList<>(); - for (NodeRef eduGroupNodeRef : eduGroupNodeRefs) { - - try { - NodeRef eduGroupHomeDir = (NodeRef) nodeService.getProperty(eduGroupNodeRef, QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR)); - - String groupName = (String) nodeService.getProperty(eduGroupNodeRef, ContentModel.PROP_AUTHORITY_NAME); - if (eduGroupHomeDir != null && nodeService.exists(eduGroupHomeDir) && authorities.contains(groupName)) { - String eduGroupHomeDirName = (String) nodeService.getProperty(eduGroupHomeDir, ContentModel.PROP_NAME); - children.add(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, nodeRef, QName.createQName(eduGroupHomeDirName), eduGroupHomeDir)); - } - } catch (InvalidNodeRefException e) { - logger.debug("eduGroupNodeRef:" + eduGroupNodeRef + " is in edugroupcache but not available over nodeservice. maybe transaction aware problem."); - } - } - return children; - } - } - return null; - }*/ - - public Collection getEduGroupNodeRefs() { - return EduGroupCache.getKeys(); - } - public List getGroupMapChildren(NodeRef nodeRef) { if (QName.createQName(CCConstants.CCM_TYPE_MAP).equals(nodeService.getType(nodeRef))) { String mapType = (String) nodeService.getProperty(nodeRef, QName.createQName(CCConstants.CCM_PROP_MAP_TYPE)); diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeDeleteEduGroupHomeDir.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeDeleteEduGroupHomeDir.java index e9d4c325a5..b4cb5f91f1 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeDeleteEduGroupHomeDir.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeDeleteEduGroupHomeDir.java @@ -1,24 +1,24 @@ package org.edu_sharing.alfresco.policy; -import java.util.List; - import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.namespace.QName; +import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.repository.client.tools.CCConstants; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; public class BeforeDeleteEduGroupHomeDir implements NodeServicePolicies.BeforeDeleteNodePolicy { PolicyComponent policyComponent; AuthorityService authorityService; AuthenticationService authenticationService; + NodeService nodeService; public static final QName TYPE_MAP = QName.createQName(CCConstants.CCM_TYPE_MAP); @@ -28,7 +28,7 @@ public void init(){ @Override public void beforeDeleteNode(NodeRef nodeRef) { - if(EduGroupCache.isAnOrganisationFolder(nodeRef)) { + if(nodeService.hasAspect(nodeRef, OrganisationService.ASPECT_EDUGROUP_FOLDER)) { if(!new Helper(authorityService).isAdmin(authenticationService.getCurrentUserName()) && !AuthenticationUtil.isRunAsUserTheSystemUser()){ throw new SystemFolderDeleteDeniedException("you are not allowed to remove this folder!"); @@ -47,4 +47,8 @@ public void setAuthorityService(AuthorityService authorityService) { public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; } + + public void setNodeService(NodeService nodeService) { + this.nodeService = nodeService; + } } diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeEduGroupDeletePolicy.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeEduGroupDeletePolicy.java deleted file mode 100644 index 32ca413c82..0000000000 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeEduGroupDeletePolicy.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.edu_sharing.alfresco.policy; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.node.NodeServicePolicies; -import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; -import org.apache.log4j.Logger; -import org.edu_sharing.repository.client.tools.CCConstants; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; - -public class BeforeEduGroupDeletePolicy implements NodeServicePolicies.BeforeDeleteNodePolicy { - - - NodeService nodeService; - PolicyComponent policyComponent; - - Logger logger = Logger.getLogger(BeforeEduGroupDeletePolicy.class); - - public void init(){ - policyComponent.bindClassBehaviour(BeforeDeleteNodePolicy.QNAME, ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "beforeDeleteNode")); - } - - @Override - public void beforeDeleteNode(NodeRef nodeRef) { - if(nodeService.hasAspect(nodeRef, QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP ))){ - - EduGroupCache.remove(nodeRef); - } - - } - - public void setNodeService(NodeService nodeService) { - this.nodeService = nodeService; - } - - public void setPolicyComponent(PolicyComponent policyComponent) { - this.policyComponent = policyComponent; - } -} diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeUpdateMap.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeUpdateMap.java index 6d2d53b39e..31ee12421d 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeUpdateMap.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/BeforeUpdateMap.java @@ -9,10 +9,8 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.namespace.QName; +import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.repository.client.tools.CCConstants; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; - -import java.util.List; public class BeforeUpdateMap implements NodeServicePolicies.BeforeUpdateNodePolicy { @@ -30,7 +28,7 @@ public void beforeUpdateNode(NodeRef nodeRef) { || authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())){ return; } - if(EduGroupCache.isAnOrganisationFolder(nodeRef)){ + if(nodeService.hasAspect(nodeRef, OrganisationService.ASPECT_EDUGROUP_FOLDER)){ throw new AccessDeniedException("Organisation Folder should not be modified!"); } } diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnAddRemoveEduGroupAspectPolicy.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnAddRemoveEduGroupAspectPolicy.java deleted file mode 100644 index 5eff4a427b..0000000000 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnAddRemoveEduGroupAspectPolicy.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.edu_sharing.alfresco.policy; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy; -import org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; -import org.apache.log4j.Logger; -import org.edu_sharing.repository.client.tools.CCConstants; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; - -public class OnAddRemoveEduGroupAspectPolicy implements OnAddAspectPolicy, OnRemoveAspectPolicy { - - NodeService nodeService; - PolicyComponent policyComponent; - - Logger logger = Logger.getLogger(OnAddRemoveEduGroupAspectPolicy.class); - - public void init(){ - //policyComponent.bindClassBehaviour(OnAddAspectPolicy.QNAME, ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "onAddAspect")); - //policyComponent.bindClassBehaviour(OnRemoveAspectPolicy.QNAME, ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "onRemoveAspect")); - - policyComponent.bindClassBehaviour(OnAddAspectPolicy.QNAME, QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP ), new JavaBehaviour(this, "onAddAspect")); - policyComponent.bindClassBehaviour(OnRemoveAspectPolicy.QNAME, QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP ), new JavaBehaviour(this, "onRemoveAspect")); - - } - - @Override - public void onAddAspect(NodeRef nodeRef, QName aspectQName) { - if(aspectQName.equals(QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP ))){ - EduGroupCache.put(nodeRef, nodeService.getProperties(nodeRef)); - } - - } - - @Override - public void onRemoveAspect(NodeRef nodeRef, QName aspectQName) { - if(aspectQName.equals(QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP ))){ - EduGroupCache.remove(nodeRef); - } - - } - - - public void setNodeService(NodeService nodeService) { - this.nodeService = nodeService; - } - - public void setPolicyComponent(PolicyComponent policyComponent) { - this.policyComponent = policyComponent; - } -} diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnCreateNodePolicyOrgAdministrators.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnCreateNodePolicyOrgAdministrators.java index 81646061c1..f77082bc80 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnCreateNodePolicyOrgAdministrators.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnCreateNodePolicyOrgAdministrators.java @@ -15,13 +15,11 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; +import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.repository.client.tools.CCConstants; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; public class OnCreateNodePolicyOrgAdministrators implements OnCreateNodePolicy, OnMoveNodePolicy { @@ -79,7 +77,7 @@ private String getAdminGroup(ChildAssociationRef childRef) { NodeRef organisationNode = null; while(!companyHome.equals(currentNode) && organisationNode == null && currentNode != null){ - if(EduGroupCache.isAnOrganisationFolder(currentNode)){ + if(nodeService.hasAspect(currentNode, OrganisationService.ASPECT_EDUGROUP_FOLDER)){ organisationNode = currentNode; break; } @@ -88,8 +86,8 @@ private String getAdminGroup(ChildAssociationRef childRef) { } if(organisationNode != null){ - - Map eduGroupProps = EduGroupCache.getByEduGroupfolder(organisationNode); + NodeRef orgNodeRef = (NodeRef)nodeService.getProperty(organisationNode,OrganisationService.PROP_EDUGROUP_FOLDER_ORGANISATION); + Map eduGroupProps = nodeService.getProperties(orgNodeRef); NodeRef eduGroupNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, (String)eduGroupProps.get(ContentModel.PROP_NODE_UUID)); List childGroups = nodeService.getChildAssocs(eduGroupNodeRef); for(ChildAssociationRef childGroup : childGroups){ diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnUpdatePersonPropertiesPolicy.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnUpdatePersonPropertiesPolicy.java index 216207deb3..cfece9b5e4 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnUpdatePersonPropertiesPolicy.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/policy/OnUpdatePersonPropertiesPolicy.java @@ -38,6 +38,8 @@ public class OnUpdatePersonPropertiesPolicy implements OnCreateNodePolicy, OnUpd Logger logger = Logger.getLogger(OnUpdatePersonPropertiesPolicy.class); private UserCache userCache; + public static ThreadLocal constructPersonFolders = new ThreadLocal<>(); + public void init(){ policyComponent.bindClassBehaviour(OnCreateNodePolicy.QNAME, ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); policyComponent.bindClassBehaviour(OnUpdateNodePolicy.QNAME, ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onUpdateNode")); @@ -99,6 +101,14 @@ public void onUpdateProperties(NodeRef nodeRef, Map before, logger.debug("will do nothing cause homeFolder is not present in after update props"); return; } + + //create esuid that will be used for user creation in remote repositories + createESUIDIfNotExists(nodeService, nodeRef); + + if(constructPersonFolders.get() != null && constructPersonFolders.get() == false){ + logger.debug("thread local constructPersonFolders is false. will skip constructPersonFolders."); + return; + } logger.debug("will create edu folders in userhome"); new HomeFolderTool(serviceRegistry).constructPersonFolders(nodeRef); @@ -106,10 +116,6 @@ public void onUpdateProperties(NodeRef nodeRef, Map before, if(HttpContext.getCurrentMetadataSet() != null) { nodeService.setProperty(homeFolderNodeRef, QName.createQName(CCConstants.CM_PROP_METADATASET_EDU_METADATASET), HttpContext.getCurrentMetadataSet()); } - //create esuid that will be used for user creation in remote repositories - - createESUIDIfNotExists(nodeService, nodeRef); - } public static boolean createESUIDIfNotExists(NodeService nodeService, NodeRef nodeRef) { diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/service/OrganisationService.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/service/OrganisationService.java index a3d56d1909..b2da3b92c3 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/service/OrganisationService.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/alfresco/service/OrganisationService.java @@ -1,10 +1,12 @@ package org.edu_sharing.alfresco.service; +import com.hazelcast.shaded.nonapi.io.github.classgraph.utils.StringUtils; import org.alfresco.model.ContentModel; import org.alfresco.repo.model.Repository; import org.alfresco.repo.node.db.DbNodeServiceImpl; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.repository.*; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AuthorityType; @@ -16,7 +18,6 @@ import org.edu_sharing.alfresco.workspace_administration.NodeServiceInterceptor; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.server.tools.cache.Cache; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; import org.edu_sharing.repository.server.tools.cache.RepositoryCache; import java.io.Serializable; @@ -32,6 +33,8 @@ public class OrganisationService { org.alfresco.service.cmr.security.AuthorityService authorityService; + org.alfresco.service.cmr.security.AuthorityService authorityServiceInsecure; + Repository repositoryHelper; PermissionService permissionService; @@ -40,11 +43,17 @@ public class OrganisationService { TransactionService transactionService; - public static String ORGANIZATION_GROUP_FOLDER = "EDU_SHARED"; + public static final String ORGANIZATION_GROUP_FOLDER = "EDU_SHARED"; public static final String CCM_PROP_EDUGROUP_EDU_HOMEDIR = "{http://www.campuscontent.de/model/1.0}edu_homedir"; public static final String CCM_PROP_EDUGROUP_EDU_UNIQUENAME = "{http://www.campuscontent.de/model/1.0}edu_uniquename"; - public static final QName QNAME_EDUGROUP = QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP); + + public static final QName ASPECT_EDUGROUP = QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP); + public static final QName PROP_EDUGROUP_EDU_HOMEDIR = QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR); + + + public static final QName ASPECT_EDUGROUP_FOLDER = QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP_FOLDER); + public static final QName PROP_EDUGROUP_FOLDER_ORGANISATION = QName.createQName(CCConstants.CCM_PROP_EDUGROUP_FOLDER_ORGANISATION); Logger logger = Logger.getLogger(OrganisationService.class); @@ -63,7 +72,7 @@ public String createOrganization(String orgName, String groupDisplayName,String NodeRef shared = getOrganisationFolderRoot(); - String orgFolderName = (groupDisplayName != null && !groupDisplayName.trim().isEmpty()) ? groupDisplayName : orgName; + String orgFolderName = !groupDisplayName.trim().isEmpty() ? groupDisplayName : orgName; orgFolderName = EduSharingNodeHelper.cleanupCmName(orgFolderName); NodeRef orgFolder = createNode(shared, CCConstants.CCM_TYPE_MAP, orgFolderName); @@ -146,20 +155,54 @@ public void syncOrganisationFolder(String authorityName){ /** * - * @param orgName without GROUP_ AND ORG_ PREFIX + * @param orgName with or without GROUP_ORG PREFIX + * + * for performance reason it uses insecure authorityServiceInsecure and dbNodeService (30%) */ public Map getOrganisation(String orgName) { - for(NodeRef nodeRef : EduGroupCache.getKeys()) { - Map props = EduGroupCache.get(nodeRef); - String tmpOrgName = getCleanName((String)props.get(ContentModel.PROP_AUTHORITY_NAME)); - if(orgName.equals(tmpOrgName)) { - return props; + + //prevent a normal group gets switched to an organisation + if(orgName.startsWith(AuthorityType.GROUP.getPrefixString()) && !hasOrganisationPrefix(orgName)){ + logger.error("orgName " + orgName + " is not an Organisation"); + return null; + } + + String authorityName = getCleanName(orgName); + authorityName = AuthorityType.GROUP.getPrefixString() + AuthorityService.ORG_GROUP_PREFIX + authorityName; + + NodeRef authorityNodeRef = authorityServiceInsecure.getAuthorityNodeRef(authorityName); + if(authorityNodeRef == null) return null; + + if(!dbNodeService.hasAspect(authorityNodeRef, ASPECT_EDUGROUP)){ + logger.error("authority: " +authorityName + " missing edugroup aspect"); + return null; + } + + return dbNodeService.getProperties(authorityNodeRef); + } + + public List> getOrganisations() { + List> organisations = new ArrayList<>(); + + logger.info("collecting authorities"); + Set authorities = authorityServiceInsecure.getAllAuthorities(AuthorityType.GROUP); + logger.info("finished collecting authorities: " + authorities.size()); + + logger.info("collecting organisations"); + for(String authorityName : authorities){ + if(hasOrganisationPrefix(authorityName)){ + Map organisation = getOrganisation(authorityName); + if(organisation != null) organisations.add(organisation); } } - - return null; + logger.info("finished collecting organisations: " + organisations.size()); + return organisations; } + private boolean hasOrganisationPrefix(String authorityName) { + return authorityName.startsWith(AuthorityType.GROUP.getPrefixString() + AuthorityService.ORG_GROUP_PREFIX); + } + public String getCleanName(String fullOrgName) { String tmpOrgName = new String(fullOrgName); tmpOrgName = tmpOrgName.replace(AuthorityType.GROUP.getPrefixString(),""); @@ -169,13 +212,14 @@ public String getCleanName(String fullOrgName) { public void syncOrganisationFolderName(boolean execute){ - for(NodeRef eduGroupFolder : EduGroupCache.getKeysEduGroupFolder()){ - Map eduGroupProps = EduGroupCache.getByEduGroupfolder(eduGroupFolder); + + for(Map eduGroupProps : getOrganisations()){ + NodeRef eduGroupFolder = (NodeRef)eduGroupProps.get(QName.createQName(CCM_PROP_EDUGROUP_EDU_HOMEDIR)); NodeRef organisationNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,(String)eduGroupProps.get(ContentModel.PROP_NODE_UUID)); String authorityName = (String)eduGroupProps.get(ContentModel.PROP_AUTHORITY_NAME); String displayName = (String)nodeService.getProperty(organisationNodeRef, ContentModel.PROP_AUTHORITY_DISPLAY_NAME); String folderName = (String)nodeService.getProperty(eduGroupFolder, ContentModel.PROP_NAME); - if (displayName == null || displayName.trim().equals("")) { + if (displayName == null || displayName.trim().isEmpty()) { logger.error("display name of authority is null or empty "+ authorityName); continue; } @@ -226,7 +270,37 @@ public void bindEduGroupFolder(String groupName, NodeRef folder) throws Exceptio params.put(QName.createQName(CCM_PROP_EDUGROUP_EDU_HOMEDIR), folder); params.put(QName.createQName(CCM_PROP_EDUGROUP_EDU_UNIQUENAME), groupName); - nodeService.addAspect(authorityNodeRef, QNAME_EDUGROUP, params); + nodeService.addAspect(authorityNodeRef, ASPECT_EDUGROUP, params); + + bindEduGroupToFolder(PermissionService.GROUP_PREFIX + groupName, folder); + } + + public void bindEduGroupToFolder(String authorityName, NodeRef folder) throws Exception{ + if(!hasOrganisationPrefix(authorityName)){ + throw new Exception("authorityName " +authorityName +" is no organisation"); + } + + if(folder == null){ + logger.error("no homefolder provided"); + return; + } + + if(!nodeService.exists(folder)){ + logger.error("folder does not exist"); + return; + } + + if(!nodeService.hasAspect(folder,ASPECT_EDUGROUP_FOLDER)){ + NodeRef authorityNodeRef = authorityService.getAuthorityNodeRef(authorityName); + if(authorityNodeRef != null){ + logger.info("adding aspect " + OrganisationService.ASPECT_EDUGROUP_FOLDER +" to the organisation folder of organisation "+ authorityName); + Map aspectProps = new HashMap<>(); + aspectProps.put(OrganisationService.PROP_EDUGROUP_FOLDER_ORGANISATION, authorityNodeRef); + nodeService.addAspect(folder,OrganisationService.ASPECT_EDUGROUP_FOLDER,aspectProps); + } + }else{ + logger.warn("folder "+ folder + " already has aspect " +ASPECT_EDUGROUP_FOLDER); + } } private NodeRef createNode(NodeRef parent, String type, String name) { @@ -365,13 +439,10 @@ public List getMyOrganisations(boolean scoped){ String eduGroupScope = (String)nodeService.getProperty(nodeRefAuthority, QName.createQName(CCConstants.CCM_PROP_EDUSCOPE_NAME)); - boolean add = false; - if(authorities.contains(CCConstants.AUTHORITY_GROUP_ALFRESCO_ADMINISTRATORS) - || authorities.contains(authority)) { - add = true; - } - - if(scoped) { + boolean add = authorities.contains(CCConstants.AUTHORITY_GROUP_ALFRESCO_ADMINISTRATORS) + || authorities.contains(authority); + + if(scoped) { String currentScope = NodeServiceInterceptor.getEduSharingScope(); if(eduGroupScope == null && currentScope != null) { add=false; @@ -390,6 +461,41 @@ public List getMyOrganisations(boolean scoped){ return organisations; } + public void unbindEduGroupFolder(String groupName, String folderId) throws Exception { + + transactionService.getRetryingTransactionHelper().doInTransaction( + + (RetryingTransactionHelper.RetryingTransactionCallback) () -> { + if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) { + + NodeRef authorityNodeRef = authorityService.getAuthorityNodeRef(PermissionService.GROUP_PREFIX + groupName); + + if (authorityNodeRef == null) { + return null; + } + + NodeRef folderNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, folderId); + + if (!nodeService.exists(folderNodeRef)) { + return null; + } + + if(!nodeService.getType(authorityNodeRef).equals(QName.createQName(CCConstants.CM_TYPE_AUTHORITY_CONTAINER))){ + throw new Exception(authorityNodeRef + " is no Group"); + } + + // remove aspect from group + nodeService.removeAspect(authorityNodeRef, OrganisationService.ASPECT_EDUGROUP); + + //remove Aspect from folder + nodeService.removeAspect(folderNodeRef,OrganisationService.ASPECT_EDUGROUP_FOLDER); + } + + return null; + }, false); + + } + public void setEduAuthorityService(AuthorityService eduAuthorityService) { this.eduAuthorityService = eduAuthorityService; } @@ -429,4 +535,8 @@ public void setTransactionService(TransactionService transactionService) { public void setDbNodeService(DbNodeServiceImpl dbNodeService) { this.dbNodeService = dbNodeService; } + + public void setAuthorityServiceInsecure(org.alfresco.service.cmr.security.AuthorityService authorityServiceInsecure) { + this.authorityServiceInsecure = authorityServiceInsecure; + } } diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueries.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueries.java index a6c5dc1311..35bd4c37ba 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueries.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueries.java @@ -24,8 +24,8 @@ public void setQueries(List queries) { public void overrideWith(MetadataQueries queries2) { if(queries2==null) return; - if(queries2.getBasequery()!=null) - setBasequery(queries2.getBasequery()); + if(queries2.basequery!=null) + setBasequery(queries2.basequery); for(MetadataQuery query: queries2.getQueries()){ if(queries!=null) { int pos = queries.lastIndexOf(query); diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueryBase.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueryBase.java index a8050694e9..1d5a28fffb 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueryBase.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataQueryBase.java @@ -23,8 +23,8 @@ public Iterable getConditions() { return conditions; } - public Map getBasequery() { - return this.basequery; + public String getPrimaryBasequery() { + return QueryUtils.replaceCommonQueryParams(this.basequery.get(null), QueryUtils.replacerFromSyntax(syntax, true)); } public void setBasequery(Map basequery) { diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataReader.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataReader.java index a66b7c54a3..d5ab2df20f 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataReader.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataReader.java @@ -545,6 +545,9 @@ private List getWidgets() throws Exception { type == null ? null : ValuespaceInfo.ValuespaceType.valueOf(type.getNodeValue()) )); } + if(name.equals("valuespaceCombineStrategy")) { + widget.setValuespaceCombineStrategy(MetadataWidget.ValuespaceMerge.valueOf(value)); + } if (name.equals("values")) widget.setValues(getValues(data.getChildNodes(), valuespaceI18n, valuespaceI18nPrefix)); if (name.equals("subwidgets")) @@ -552,17 +555,24 @@ private List getWidgets() throws Exception { } if(valuespaces.size() > 1) { List keys = new ArrayList<>(); - for (ValuespaceInfo v : valuespaces) { - ValuespaceData values = getValuespace(v, widget, valuespaceI18n, valuespaceI18nPrefix); - if(values.getTitle() == null) { - throw new IllegalArgumentException("Multiple valuespace entries are not supported by the given provider used for your vocabularies"); - } - values.getEntries().stream().filter(e -> e.getParent() == null).forEach(e -> { - e.setParent(values.getTitle().getKey()); - }); - keys.add(values.getTitle()); - keys.addAll(values.getEntries()); - } + if(widget.getValuespaceMerge().equals(MetadataWidget.ValuespaceMerge.separate)) { + for (ValuespaceInfo v : valuespaces) { + ValuespaceData values = getValuespace(v, widget, valuespaceI18n, valuespaceI18nPrefix); + if (values.getTitle() == null) { + throw new IllegalArgumentException("Multiple valuespace entries are not supported by the given provider used for your vocabularies"); + } + values.getEntries().stream().filter(e -> e.getParent() == null).forEach(e -> { + e.setParent(values.getTitle().getKey()); + }); + keys.add(values.getTitle()); + keys.addAll(values.getEntries()); + } + } else { + for (ValuespaceInfo v : valuespaces) { + ValuespaceData values = getValuespace(v, widget, valuespaceI18n, valuespaceI18nPrefix); + keys.addAll(values.getEntries()); + } + } widget.setValues(keys); } else if (valuespaces.size() == 1) { widget.setValues(getValuespace(valuespaces.get(0),widget,valuespaceI18n,valuespaceI18nPrefix).getEntries()); diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataWidget.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataWidget.java index 8efcc5c519..e9e58c4b2c 100644 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataWidget.java +++ b/Backend/alfresco/module/src/main/java/org/edu_sharing/metadataset/v2/MetadataWidget.java @@ -18,6 +18,7 @@ public class MetadataWidget extends MetadataTranslatable { private String configuration; + private ValuespaceMerge valuespaceMerge = ValuespaceMerge.separate; public enum Required { mandatory, @@ -47,7 +48,16 @@ public enum WidgetFilterMode { auto, always } - + public enum ValuespaceMerge { + /** + * all valuespaces are presented as an invidual tree on the main level (default) + */ + separate, + /** + * all valuespaces are merged into one list/tree + */ + merge, + } public enum TextEscapingPolicy { // no escaping, strongly discouraged since it can allow XSS vulnerabilities if the data comes from untrusted sources none, @@ -190,4 +200,12 @@ public TreeNode getValuespaceTree() { return TreeNode.of(values.values(), MetadataKey::getKey, MetadataKey::getParent); } + + public void setValuespaceCombineStrategy(ValuespaceMerge valuespaceMerge) { + this.valuespaceMerge = valuespaceMerge; + } + + public ValuespaceMerge getValuespaceMerge() { + return valuespaceMerge; + } } diff --git a/Backend/alfresco/module/src/main/java/org/edu_sharing/repository/server/tools/cache/EduGroupCache.java b/Backend/alfresco/module/src/main/java/org/edu_sharing/repository/server/tools/cache/EduGroupCache.java deleted file mode 100644 index d6f4f26a7b..0000000000 --- a/Backend/alfresco/module/src/main/java/org/edu_sharing/repository/server/tools/cache/EduGroupCache.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.edu_sharing.repository.server.tools.cache; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.namespace.QName; -import org.apache.log4j.Logger; -import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; -import org.edu_sharing.repository.client.tools.CCConstants; - -import java.io.Serializable; -import java.util.*; - - - -public class EduGroupCache { - - public static Logger logger = Logger.getLogger(EduGroupCache.class); - - /** - * Administration by: - * org.edu_sharing.repository.server.tools.EduGroupTool: put when aspect added, remove when aspect removed - * org.edu_sharing.alfresco.policy.BeforeEduGroupDeletePolicy remove when edugroup will be deleted - * org.edu_sharing.repository.server.MCAlfrescoManager: init when webapp is started - * - */ - - - public static ServiceRegistry serviceRegistry = (ServiceRegistry)AlfAppContextGate.getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); - - // Shared Cache over EHCache - final static SimpleCache> cache = (SimpleCache>) AlfAppContextGate.getApplicationContext().getBean("eduSharingEduGroupCache"); - - final static SimpleCache> cacheWithFolderAsKey = (SimpleCache>) AlfAppContextGate.getApplicationContext().getBean("eduSharingEduGroupFolderCache"); - - public static void put(NodeRef nodeRef, Map props){ - - synchronized(EduGroupCache.cache){ - EduGroupCache.cache.put(nodeRef, props); - EduGroupCache.putEduGroupFolder((NodeRef)props.get(QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR)),props); - } - - } - - private static void putEduGroupFolder(NodeRef nodeRef, Map props){ - if(nodeRef == null){ - String message = "got a null value for edugroupfolder of edu group:"; - if(props != null) message += props.get(ContentModel.PROP_AUTHORITY_NAME); - logger.error(message); - return; - } - EduGroupCache.cacheWithFolderAsKey.put(nodeRef,props); - } - - public static Map get(NodeRef nodeRef) { - return EduGroupCache.cache.get(nodeRef); - } - - public static Map getByEduGroupfolder(NodeRef nodeRef){ - return EduGroupCache.cacheWithFolderAsKey.get(nodeRef); - } - - public static void remove(NodeRef nodeRef) { - synchronized(EduGroupCache.cache){ - NodeRef nodeRefFolder = (NodeRef)EduGroupCache.cache.get(nodeRef).get(QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR)); - if(nodeRefFolder != null) { - EduGroupCache.cacheWithFolderAsKey.remove(nodeRefFolder); - } - EduGroupCache.cache.remove(nodeRef); - } - } - - public static Collection getKeys(){ - return EduGroupCache.cache.getKeys(); - } - - public static Collection getKeysEduGroupFolder(){ - return EduGroupCache.cacheWithFolderAsKey.getKeys(); - } - - public static String[] getNames(){ - ArrayList names = new ArrayList<>(); - for(NodeRef nodeRef : cache.getKeys()){ - names.add((String)cache.get(nodeRef).get(ContentModel.PROP_AUTHORITY_NAME)); - } - return names.toArray(new String[names.size()]); - } - - public static boolean isAnOrganisationFolder(NodeRef nodeRef){ - if(EduGroupCache.cacheWithFolderAsKey.contains(nodeRef)) return true; - else return false; - } - - public static void refresh(){ - synchronized(EduGroupCache.cache){ - logger.info("size before refresh:"+EduGroupCache.cache.getKeys().size()); - clear(); - NodeService nodeServiceAlfresco = (NodeService) AlfAppContextGate.getApplicationContext().getBean("alfrescoDefaultDbNodeService"); - serviceRegistry.getRetryingTransactionHelper().doInTransaction(() -> { - for (NodeRef eduGroupNodeRef : getEduGroupNodeRefs()) { - Map properties = nodeServiceAlfresco.getProperties(eduGroupNodeRef); - EduGroupCache.put(eduGroupNodeRef, properties); - } - return null; - }); - logger.info("size after refresh:"+EduGroupCache.cache.getKeys().size()); - } - } - - private static void clear(){ - EduGroupCache.cache.clear(); - EduGroupCache.cacheWithFolderAsKey.clear(); - } - - public static void refreshByKeepExisting(){ - synchronized(EduGroupCache.cache){ - logger.info("size before refresh:"+EduGroupCache.cache.getKeys().size()); - //EduGroupCache.cache.clear(); - for(NodeRef eduGroupNodeRef : getEduGroupNodeRefs()){ - if(!EduGroupCache.cache.contains(eduGroupNodeRef)) { - Map properties = serviceRegistry.getNodeService().getProperties(eduGroupNodeRef); - EduGroupCache.put(eduGroupNodeRef, properties); - } - } - logger.info("size after refresh:"+EduGroupCache.cache.getKeys().size()); - } - } - - private static List getEduGroupNodeRefs(){ - logger.info("starting"); - AuthorityService authorityService =serviceRegistry.getAuthorityService(); - NodeService nodeServiceAlfresco = (NodeService) AlfAppContextGate.getApplicationContext().getBean("alfrescoDefaultDbNodeService"); - List result = new ArrayList<>(); - Set allGroups = authorityService.getAllAuthoritiesInZone(AuthorityService.ZONE_APP_DEFAULT, AuthorityType.GROUP); - for(String authority : allGroups) { - NodeRef authorityNodeRef = authorityService.getAuthorityNodeRef(authority); - if(nodeServiceAlfresco.hasAspect(authorityNodeRef, QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP))) { - result.add(authorityNodeRef); - } - } - logger.info("found groupCount: " + allGroups.size() + " eduGroupCount:" + result.size() ); - return result; - - } - -} diff --git a/Backend/services/core/src/main/java/org/alfresco/repo/webdav/auth/LDAPAuthenticationFilter.java b/Backend/services/core/src/main/java/org/alfresco/repo/webdav/auth/LDAPAuthenticationFilter.java index 24415bba2c..1808aa6ad7 100644 --- a/Backend/services/core/src/main/java/org/alfresco/repo/webdav/auth/LDAPAuthenticationFilter.java +++ b/Backend/services/core/src/main/java/org/alfresco/repo/webdav/auth/LDAPAuthenticationFilter.java @@ -40,6 +40,9 @@ import java.util.*; +/** + * Servlet Filter implementation class CockpitAuthenticationFilter + */ /** * Servlet Filter implementation class CockpitAuthenticationFilter */ @@ -68,7 +71,7 @@ public class LDAPAuthenticationFilter implements Filter, DependencyInjectedFilte * edu-sharing customization */ private static final String INIT_USE_ALFRESCO_AUTHENTICATION_COMPONENT = INIT_CONFIG_BASE + "ldap.alfrescoAuthComponent"; - + // Allow an authentication ticket to be passed as part of a request to bypass authentication @@ -99,48 +102,48 @@ public class LDAPAuthenticationFilter implements Filter, DependencyInjectedFilte private boolean useAlfrescoAuthenticationComponent = false; private String ldapBase = null; - + //rember the env global private Properties env = null; private String ldapUidProp = null; private String ldapUrl = null; - - + + /** * edu-sharing fix from 4.2.f - * - * + * + * * ALF-13621: Due to browser inconsistencies we have to try a fallback path of encodings */ /** The password encodings to try in priority order **/ - private static final String[] ENCODINGS = new String[] { - "UTF-8", - System.getProperty("file.encoding"), - "ISO-8859-1" - }; - - /** Corresponding array of CharsetDecoders with CodingErrorAction.REPORT. Duplicates removed. */ - private static final CharsetDecoder[] DECODERS; - - static - { + private static final String[] ENCODINGS = new String[] { + "UTF-8", + System.getProperty("file.encoding"), + "ISO-8859-1" + }; + + /** Corresponding array of CharsetDecoders with CodingErrorAction.REPORT. Duplicates removed. */ + private static final CharsetDecoder[] DECODERS; + + static + { Map decoders = new LinkedHashMap<>(ENCODINGS.length * 2); - for (String encoding : ENCODINGS) - { - if (!decoders.containsKey(encoding)) - { - decoders.put(encoding, Charset.forName(encoding).newDecoder() - .onMalformedInput(CodingErrorAction.REPORT)); - } - } - DECODERS = new CharsetDecoder[decoders.size()]; - decoders.values().toArray(DECODERS); - } - - + for (String encoding : ENCODINGS) + { + if (!decoders.containsKey(encoding)) + { + decoders.put(encoding, Charset.forName(encoding).newDecoder() + .onMalformedInput(CodingErrorAction.REPORT)); + } + } + DECODERS = new CharsetDecoder[decoders.size()]; + decoders.values().toArray(DECODERS); + } + + /** * Initialize the filter - * + * * @param config FitlerConfig * @exception ServletException */ @@ -156,7 +159,7 @@ public void init(FilterConfig config) throws ServletException // Setup the authentication context //WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.m_context); - + ApplicationContext context = AlfAppContextGate.getApplicationContext(); ServiceRegistry serviceRegistry = (ServiceRegistry) context.getBean(ServiceRegistry.SERVICE_REGISTRY); @@ -184,6 +187,7 @@ public void init(FilterConfig config) throws ServletException env.put(Context.SECURITY_AUTHENTICATION, eduConfig.getString(LDAPAuthenticationFilter.INIT_LDAP_SEC_AUTH)); env.put(Context.SECURITY_PRINCIPAL, eduConfig.getString(LDAPAuthenticationFilter.INIT_LDAP_SEC_USER)); env.put(Context.SECURITY_CREDENTIALS, eduConfig.getString(LDAPAuthenticationFilter.INIT_LDAP_SEC_PWD)); + } /** @@ -209,7 +213,7 @@ public void doFilter(ServletContext servletContext, ServletRequest servletReques } /** * Run the authentication filter - * + * * @param req ServletRequest * @param resp ServletResponse * @param chain FilterChain @@ -218,7 +222,7 @@ public void doFilter(ServletContext servletContext, ServletRequest servletReques */ @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, - ServletException + ServletException { if(this.jndi == null){ try { @@ -243,7 +247,7 @@ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain { // Get the authorization header - + String authHdr = httpReq.getHeader("Authorization"); logger.debug("user == null authHdr:"+authHdr); if ( (authHdr != null) && (authHdr.length() > 5) && authHdr.substring(0,5).equalsIgnoreCase("BASIC")) @@ -277,118 +281,119 @@ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain this.jndi = new InitialDirContext(env); user = searchForUser(username,password); }catch(CommunicationException ce){ - + logger.error(e.getMessage() + " still occurs will give up. maybe restart alfresco."); - + }catch (NamingException ne) { logger.error(ne.getMessage(), ne); } } - + if(user != null){ httpReq.getSession().setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, user); } - + */ - + /** * edu-sharing fix */ - if (logger.isDebugEnabled()) - logger.debug("Basic authentication details present in the header."); - byte[] encodedString = java.util.Base64.getDecoder().decode(authHdr.substring(5).trim().getBytes()); - - // ALF-13621: Due to browser inconsistencies we have to try a fallback path of encodings - Set attemptedAuths = new HashSet(DECODERS.length * 2); - for (CharsetDecoder decoder : DECODERS) - { - try - { - // Attempt to decode using this charset - String basicAuth = decoder.decode(ByteBuffer.wrap(encodedString)).toString(); - - - - // It decoded OK but we may already have tried this string. - if (!attemptedAuths.add(basicAuth)) - { - // Already tried - no need to try again - continue; - } - - - String username = null; - String password = null; - - - // Split the username and password - int pos = basicAuth.indexOf(":"); - if (pos != -1) - { - username = basicAuth.substring(0, pos); - password = basicAuth.substring(pos + 1); - } - else - { - username = basicAuth; - password = ""; - } - - // Authenticate the user - try{ - user = searchForUser(username,password); - }catch(CommunicationException e){ - logger.error(e.getMessage() +" Will create new InitialDirContext and retry."); - try{ - this.jndi = new InitialDirContext(env); - user = searchForUser(username,password); - }catch(CommunicationException ce){ - - logger.error(e.getMessage() + " still occurs will give up. maybe restart alfresco."); - - }catch (NamingException ne) { - logger.error(ne.getMessage(), ne); - } - } - - if(user != null){ - httpReq.getSession().setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, user); - // Success so break out - break; - } - - - - - } - catch (CharacterCodingException e) - { - if (logger.isDebugEnabled()) - logger.debug("Didn't decode using " + decoder.getClass().getName(), e); - } - catch (AuthenticationException ex) - { - if (logger.isDebugEnabled()) - logger.debug("Authentication error ", ex); - } - catch (NoSuchPersonException e) - { - if (logger.isDebugEnabled()) - logger.debug("There is no such person error ", e); - } - } - - - - + if (logger.isDebugEnabled()) + logger.debug("Basic authentication details present in the header."); + byte[] encodedString = java.util.Base64.getDecoder().decode(authHdr.substring(5).trim().getBytes()); + + // ALF-13621: Due to browser inconsistencies we have to try a fallback path of encodings + Set attemptedAuths = new HashSet(DECODERS.length * 2); + for (CharsetDecoder decoder : DECODERS) + { + try + { + // Attempt to decode using this charset + String basicAuth = decoder.decode(ByteBuffer.wrap(encodedString)).toString(); + + + + // It decoded OK but we may already have tried this string. + if (!attemptedAuths.add(basicAuth)) + { + // Already tried - no need to try again + continue; + } + + + String username = null; + String password = null; + + + // Split the username and password + int pos = basicAuth.indexOf(":"); + if (pos != -1) + { + username = basicAuth.substring(0, pos); + password = basicAuth.substring(pos + 1); + } + else + { + username = basicAuth; + password = ""; + } + + // Authenticate the user + try{ + logger.info("webdav ldap authentication: starting. loginName:####"); + user = searchForUser(username,password); + }catch(CommunicationException e){ + logger.error(e.getMessage() +" Will create new InitialDirContext and retry."); + try{ + this.jndi = new InitialDirContext(env); + user = searchForUser(username,password); + }catch(CommunicationException ce){ + logger.error(e.getMessage() + " still occurs will give up. maybe restart alfresco."); + }catch (NamingException ne) { + logger.error(ne.getMessage(), ne); + } + }catch(NoSuchPersonException e){ + logger.error("person does not exist in alfresco"); + } + + if(user != null){ + httpReq.getSession().setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, user); + // Success so break out + break; + } + + + + + } + catch (CharacterCodingException e) + { + if (logger.isDebugEnabled()) + logger.debug("Didn't decode using " + decoder.getClass().getName(), e); + } + catch (AuthenticationException ex) + { + if (logger.isDebugEnabled()) + logger.debug("Authentication error ", ex); + } + catch (NoSuchPersonException e) + { + if (logger.isDebugEnabled()) + logger.debug("There is no such person error ", e); + } + } + + + + } else { // Check if the request includes an authentication ticket String ticket = req.getParameter( LDAPAuthenticationFilter.ARG_TICKET); - + logger.debug("auth by ticket:"+ticket); if ( (ticket != null) && (ticket.length() > 0)) @@ -490,7 +495,7 @@ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain } else { - + logger.debug("user != null :"+user.getTicket()); try { @@ -517,20 +522,22 @@ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain chain.doFilter(req, resp); } - + /** * search for user encapsulated in a method to catch a potential CommunicationException and retry - * + * *https://issues.apache.org/jira/browse/HADOOP-9125 *https://issues.apache.org/jira/secure/attachment/12560771/HADOOP-9125.patch */ - WebDAVUser searchForUser(String username, String password) throws CommunicationException{ - - - String ldapUsername = username; - + WebDAVUser searchForUser(String loginName, String password) throws CommunicationException{ + + String uid = null; - + + String dn = null; + + String username = loginName; + try { SearchControls ctls = new SearchControls(); @@ -554,11 +561,16 @@ WebDAVUser searchForUser(String username, String password) throws CommunicationE if (attr != null) { username = (String) attr.get(); } - + Attribute uidAttr = r.getAttributes().get(this.ldapUidProp); if(uidAttr != null){ uid = (String) uidAttr.get(); } + + dn = r.getNameInNamespace(); + + }else{ + throw new AuthenticationException("webdav ldap authentication: user not found in directory. loginName:###"); } rs.close(); @@ -567,7 +579,7 @@ WebDAVUser searchForUser(String username, String password) throws CommunicationE if(username != null){ final String fusername = username; boolean allowed = AuthenticationUtil.runAsSystem(() -> { - NodeRef personRef = this.m_personService.getPerson(fusername); + NodeRef personRef = this.m_personService.getPerson(fusername, false); if(!LightbendConfigLoader.get().getIsNull("repository.personActiveStatus")) { String personActiveStatus = LightbendConfigLoader.get().getString("repository.personActiveStatus"); String personStatus = (String)this.m_nodeService.getProperty(personRef, QName.createQName(CCConstants.CM_PROP_PERSON_ESPERSONSTATUS)); @@ -579,17 +591,16 @@ WebDAVUser searchForUser(String username, String password) throws CommunicationE return true; }); if(!allowed){ - throw new AuthenticationException("USER_BLOCKED"); + throw new AuthenticationException("webdav ldap authentication: USER_BLOCKED. loginName: ### / userName: ###"); } - } if(useAlfrescoAuthenticationComponent){ this.m_authService.authenticate(username, password.toCharArray()); }else{ - - logger.debug("using direct ldap auth ldapUsername:" + ldapUsername + " uid:" +uid +" username:" +username +" password:" +password); - this.authenticate(ldapUsername, uid, username, password); + + logger.debug("using ldap auth dn:" + dn + " uid:" +uid +" username:" +username); + this.authenticate(dn, username, password, loginName); } // Set the user name as stored by the back end @@ -604,7 +615,7 @@ WebDAVUser searchForUser(String username, String password) throws CommunicationE return new WebDAVUser(username, this.m_authService.getCurrentTicket(), homeSpaceRef); - + }catch(CommunicationException ce){ throw ce; @@ -613,53 +624,61 @@ WebDAVUser searchForUser(String username, String password) throws CommunicationE logger.error(e.getMessage(),e); } catch (AuthenticationException ex) { // Do nothing, user object will be null - logger.error(ex.getMessage(),ex); - } catch (NoSuchPersonException e) { - // Do nothing, user object will be null - logger.error(e.getMessage(),e); + if(ex.getMessage() != null && ex.getMessage().contains("Invalid Credentials")){ + logger.warn("webdav ldap authentication: failed with Invalid Credentials. loginName: ### / userName: ###"); + } + if (ex.getMessage() != null && ex.getMessage().contains("DN with no password")) { + logger.warn("webdav ldap authentication: no password provided. loginName: ### / userName: ###"); + }else { + logger.warn(ex.getMsgId()); + if (logger.isDebugEnabled()) { + logger.error(ex.getMessage(), ex); + } + } } - + return null; } - + /** - * edu-sharing customization: try to authenticate at ldap directly - * - * @param ldapUsername + * edu-sharing customization: try to authenticate at ldap directly + * * @param username * @param password + * @param loginName * @throws AuthenticationException */ - private void authenticate(String ldapUsername, String ldapUid, String username, String password) throws AuthenticationException{ - + private void authenticate(String ldapUserDn, String username, String password, String loginName) throws AuthenticationException{ + if(env != null){ Properties authEnv = new Properties(); authEnv.put(Context.INITIAL_CONTEXT_FACTORY, - "com.sun.jndi.ldap.LdapCtxFactory"); + "com.sun.jndi.ldap.LdapCtxFactory"); //authEnv.put(Context.PROVIDER_URL, env.get(Context.PROVIDER_URL)); authEnv.put(Context.PROVIDER_URL,this.ldapUrl); //authEnv.put(Context.SECURITY_PRINCIPAL,"uid="+ldapUid); - authEnv.put(Context.SECURITY_PRINCIPAL,"uid="+ldapUid+","+this.ldapBase); - + //authEnv.put(Context.SECURITY_PRINCIPAL,"uid="+ldapUid+","+this.ldapBase); + authEnv.put(Context.SECURITY_PRINCIPAL,ldapUserDn); + authEnv.put(Context.SECURITY_AUTHENTICATION,env.get(Context.SECURITY_AUTHENTICATION)); authEnv.put(Context.SECURITY_CREDENTIALS,password); - - + + try { new InitialDirContext(authEnv); ApplicationContext context = AlfAppContextGate.getApplicationContext(); AuthenticationComponent authComp = (AuthenticationComponent)context.getBean("authenticationComponent"); authComp.setCurrentUser(username); - logger.info("auth at ldap sucessfull with user:"+username); + logger.info("webdav ldap authentication: sucessfull. loginName: ### / userName: ###"); return; }catch(javax.naming.AuthenticationException e){ - logger.error(e.getMessage(), e); + logger.debug(e.getMessage(), e); throw new AuthenticationException(e.getMessage()); } catch (NamingException e) { - logger.error(e.getMessage(), e); + logger.debug(e.getMessage(), e); throw new AuthenticationException(e.getMessage()); } - + } throw new AuthenticationException("LDAPAuthenticationFilter env seems to be null"); } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/alfrescocontext/AlfrescoBeanConfig.java b/Backend/services/core/src/main/java/org/edu_sharing/alfrescocontext/AlfrescoBeanConfig.java index 2adb5a2a75..641cc08f99 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/alfrescocontext/AlfrescoBeanConfig.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/alfrescocontext/AlfrescoBeanConfig.java @@ -37,6 +37,7 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; import org.edu_sharing.alfresco.policy.HomeFolderTool; +import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; @@ -269,9 +270,9 @@ public WorkflowService workflowService() { return serviceRegistry.getWorkflowService(); } - @Bean(name="WebDavAuthenticationFilter") - @ConditionalOnMissingBean(name = "WebDavAuthenticationFilter") - public DependencyInjectedFilter webDavAuthenticationFilter(){ return applicationContext.getBean("WebDavAuthenticationFilter", DependencyInjectedFilter.class); } + @Bean(name="WebDavAuthenticationFilter") + @ConditionalOnMissingBean(name = "WebDavAuthenticationFilter") + public DependencyInjectedFilter webDavAuthenticationFilter(){ return applicationContext.getBean("WebDavAuthenticationFilter", DependencyInjectedFilter.class); } @Bean public NodeService alfrescoDefaultDbNodeService() { @@ -309,4 +310,7 @@ public ModuleService moduleServiceWithoutSecurity() { return serviceRegistry.getModuleService(); } + @Bean(name = "organisationService") + public OrganisationService organisationService() {return (OrganisationService)applicationContext.getBean("eduOrganisationService");} + } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataElasticSearchHelper.java b/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataElasticSearchHelper.java index 16d3c01f80..8abd8ccaf7 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataElasticSearchHelper.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataElasticSearchHelper.java @@ -41,7 +41,7 @@ public static BoolQuery.Builder getElasticSearchQuery(SearchToken searchToken, M BoolQuery.Builder result = new BoolQuery.Builder(); if(asFilter == null || (asFilter == query.getBasequeryAsFilter())) { - String baseQuery = replaceCommonQueryVariables(query.getBasequery().get(null)); + String baseQuery = replaceCommonQueryVariables(query.getPrimaryBasequery()); String baseQueryConditional = replaceCommonQueryVariables(query.findBasequery(parameters == null ? null : parameters.keySet())); if(Objects.equals(baseQuery,baseQueryConditional)) { diff --git a/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataTemplateRenderer.java b/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataTemplateRenderer.java index b31accdf92..d4c2166e5f 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataTemplateRenderer.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/metadataset/v2/tools/MetadataTemplateRenderer.java @@ -86,8 +86,7 @@ public static Map convertProps(Map props) { Map propsConverted = new HashMap<>(); for(String key : props.keySet()){ String keyLocal= CCConstants.getValidLocalName(key); - - if(props.get(key) == null) continue; + if(keyLocal == null || props.get(key) == null) continue; String[] values=ValueTool.getMultivalue(props.get(key).toString()); propsConverted.put(keyLocal, values); @@ -448,7 +447,6 @@ private boolean renderMaterialFeedback(MetadataWidget widget, StringBuffer widge +" "+PermissionServiceFactory.getLocalService().hasPermission(StoreRef.PROTOCOL_WORKSPACE,StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier(),parent,CCConstants.PERMISSION_DELETE)); if( ToolPermissionServiceFactory.getInstance().hasToolPermission(CCConstants.CCM_VALUE_TOOLPERMISSION_MATERIAL_FEEDBACK) && - !GuestCagePolicy.getGuestUsers().contains(userName) && PermissionServiceFactory.getLocalService().hasPermission(StoreRef.PROTOCOL_WORKSPACE,StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier(),nodeRef.getId(),CCConstants.PERMISSION_FEEDBACK) && !PermissionServiceFactory.getLocalService().hasPermission(StoreRef.PROTOCOL_WORKSPACE,StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier(),nodeRef.getId(),CCConstants.PERMISSION_DELETE) ){ diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/client/tools/I18nAngular.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/client/tools/I18nAngular.java index 9c946bddbc..cb2f4f054a 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/client/tools/I18nAngular.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/client/tools/I18nAngular.java @@ -18,16 +18,21 @@ public class I18nAngular { public static final String GENDER_SEPARATOR = "*"; public static Logger logger=Logger.getLogger(I18nAngular.class); public static String getTranslationAngular(String scope,String key){ - return getTranslationAngular(scope,key,new AuthenticationToolAPI().getCurrentLanguage()); + return getTranslationAngular(scope,key,new AuthenticationToolAPI().getCurrentAngularLanguage()); } public static JSONObject getLanguageStrings() throws Exception{ - String language=new AuthenticationToolAPI().getCurrentLanguage(); + String language=new AuthenticationToolAPI().getCurrentAngularLanguage(); ServletContext context = Context.getCurrentInstance().getRequest().getSession().getServletContext(); File[] dirs = new File(context.getRealPath("/assets/i18n/")).listFiles(File::isDirectory); JSONObject result=new JSONObject(); for(File dir : dirs) { File i18n = new File(dir, language + ".json"); + // fallback to the base language + if(language.startsWith("de-") && !i18n.exists()) { + // ignore missing files + continue; + } String json = FileUtils.readFileToString(i18n,"UTF-8"); JSONObject jsonObject = new JSONObject(json); try { @@ -58,14 +63,23 @@ private static String getTranslationAngular(String scope,String key,String langu logger.debug("Trying to fetch angular translation before context initalization"); return key; } - String json=FileUtils.readFileToString(new File(servletContext.getRealPath("/assets/i18n/"+scope+"/"+language+".json")),"UTF-8"); + File i18nFile = new File(servletContext.getRealPath("/assets/i18n/"+scope+"/"+language+".json")); + // fallback to the base language + if(language.startsWith("de-") && !i18nFile.exists()) { + return getTranslationAngular(scope, key, "de"); + } + String json=FileUtils.readFileToString(i18nFile,"UTF-8"); JSONObject object=new JSONObject(json); String[] list=key.split("\\."); for(int i=0;i eduGroupNodeRefs = new VirtualEduGroupFolderTool(serviceRegistry, nodeService).getEduGroupNodeRefs(); - - // nodeRefEduGroupFolder , noderefEduGroup - Map eduGroupEduGroupFolderMap = new HashMap<>(); - for (NodeRef eduGroupNodeRef : eduGroupNodeRefs) { - eduGroupEduGroupFolderMap.put((NodeRef) nodeService.getProperty(eduGroupNodeRef, QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR)), - eduGroupNodeRef); - } - - Group group = null; - try { - do { - ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef); - nodeRef = (parentAssocRef == null) ? null : parentAssocRef.getParentRef(); - if (nodeRef != null) { - nodeType = nodeService.getType(nodeRef); - } - - NodeRef groupNodeRef = eduGroupEduGroupFolderMap.get(nodeRef); - if ((groupNodeRef != null)) { - result = groupNodeRef; - - } - - } while (nodeRef != null && mapType.equals(nodeType) && result == null); - - if (result != null) { - group = new Group(); - String authorityName = (String) nodeService.getProperty(result, ContentModel.PROP_AUTHORITY_NAME); - group.setName(authorityName); - group.setDisplayName((String) nodeService.getProperty(result, ContentModel.PROP_AUTHORITY_DISPLAY_NAME)); - group.setRepositoryId(appInfo.getAppId()); - group.setNodeId(result.getId()); - group.setAuthorityType(AuthorityType.getAuthorityType(group.getName()).name()); - group.setScope((String) nodeService.getProperty(nodeRef, QName.createQName(CCConstants.CCM_PROP_SCOPE_TYPE))); - NodeRef authorityNodeRef = authorityService.getAuthorityNodeRef(authorityName); - if (authorityNodeRef != null) { - String groupType = (String) nodeService.getProperty(authorityNodeRef, QName.createQName(CCConstants.CCM_PROP_GROUPEXTENSION_GROUPTYPE)); - if (groupType != null) { - group.setGroupType(groupType); - } - } - } - } catch (org.alfresco.repo.security.permissions.AccessDeniedException e) { - // maybe while doing nodeService.getPrimaryParent(nodeRef); and - // landing in an folder where i have no read permissions - log.debug(e.getMessage()); - } - - return group; - } - public Map checkAndCreateShadowUser(String username, String email, String repId) throws Exception { throw new Exception("checkAndCreateShadowUser is not implemented!"); } @@ -3990,37 +3922,6 @@ public Void execute() throws Throwable { } } - public void unbindEduGroupFolder(String groupName, String folderId) throws Exception { - - serviceRegistry.getTransactionService().getRetryingTransactionHelper().doInTransaction( - - new RetryingTransactionCallback() { - public Void execute() throws Throwable { - if (isAdmin()) { - - AuthorityService authorityService = serviceRegistry.getAuthorityService(); - NodeRef authorityNodeRef = authorityService.getAuthorityNodeRef(PermissionService.GROUP_PREFIX + groupName); - - if (authorityNodeRef == null) { - return null; - } - - NodeService nodeService = serviceRegistry.getNodeService(); - NodeRef folderNodeRef = new NodeRef(storeRef, folderId); - - if (!nodeService.exists(folderNodeRef)) { - return null; - } - - EduGroupTool.processEduGroupMicroCommand("COMMAND REMOVE " + authorityNodeRef.toString() + " " + folderNodeRef.toString()); - } - - return null; - } - }, false); - - } - public InputStream getContent(String nodeId) { return getContent(nodeId, CCConstants.CM_PROP_CONTENT); } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClient.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClient.java index 08eba4545d..b9a2d27b1e 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClient.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClient.java @@ -319,15 +319,7 @@ enum ContextSearchMode{ */ public void moveNode(String newParentId, String childAssocType, String nodeId) throws Exception; - - /** - * - * @param nodeId: the nodeId from which to start to travers through parents until a homefolder of an edugroup is found - * @return - */ - public Group getEduGroupContextOfNode(String nodeId); - /** * create a shadow user in the remote repository if he is not already there diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClientAdapter.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClientAdapter.java index 5421b88dc3..3bf9c450f9 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClientAdapter.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoClientAdapter.java @@ -161,11 +161,6 @@ public String getRootNodeId() throws Exception { public void moveNode(String newParentId, String childAssocType, String nodeId) throws Exception { } - @Override - public Group getEduGroupContextOfNode(String nodeId) { - return null; - } - @Override public Map checkAndCreateShadowUser(String username, String email, String repId) throws Exception { diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoManager.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoManager.java index f5b876c734..a4676fb9fa 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoManager.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/MCAlfrescoManager.java @@ -27,10 +27,7 @@ */ package org.edu_sharing.repository.server; -import java.io.File; - import jakarta.servlet.ServletContextEvent; - import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.NodeRef; import org.apache.chemistry.opencmis.server.impl.CmisRepositoryContextListener; @@ -44,7 +41,6 @@ import org.edu_sharing.repository.server.tools.ApplicationInfoList; import org.edu_sharing.repository.server.tools.InitHelper; import org.edu_sharing.repository.server.tools.UserEnvironmentTool; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; import org.edu_sharing.repository.server.tracking.TrackingService; import org.edu_sharing.repository.server.tracking.TrackingService.TrackingBufferFactory; import org.edu_sharing.repository.server.tracking.buffer.FileRingBuffer; @@ -56,6 +52,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.web.context.ContextLoaderListener; +import java.io.File; + public class MCAlfrescoManager extends ContextLoaderListener { Log logger = LogFactory.getLog(MCAlfrescoManager.class); @@ -87,18 +85,6 @@ public void contextInitialized(ServletContextEvent servletContextEvent) { super.setContextInitializers(new EduSharingContextInitializer()); super.contextInitialized(servletContextEvent); - logger.info("load edu groups"); - - /** - * only refresh when size is null, to prevent that all clusternodes try to clear and fill again, so in best case only the first cluster node fill's this - */ - if(EduGroupCache.getKeys().size() == 0){ - logger.info("starting filling edugroup cache"); - EduGroupCache.refresh(); - }else{ - logger.info("edugroup cache has "+EduGroupCache.getKeys().size() +" entries, getting(got) cache entries by another cluster node"); - } - //init the system folders so that are created with a admin @@ -114,6 +100,11 @@ public void contextInitialized(ServletContextEvent servletContextEvent) { }catch(Throwable t) { logger.error("init of config groups failed: " + t.getMessage(), t); } + try { + InitHelper.initPersons(); + }catch(Throwable t) { + logger.error("init of config persons failed: " + t.getMessage(), t); + } //init ToolPermisssions ToolPermissionServiceFactory.getInstance().init(); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/NgServlet.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/NgServlet.java index 6514e993ec..c9ad029182 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/NgServlet.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/NgServlet.java @@ -144,7 +144,7 @@ private static String addLicenseMetadata(String html, URL url) { return addToHead(data, html); } }catch(Throwable t){ - logger.error("Failed to load node license for attaching to head:",t); + logger.warn("Failed to load node license for attaching to head:",t); } return html; } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/SecurityHeadersFilter.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/SecurityHeadersFilter.java index a439fe3e3b..fff358768d 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/SecurityHeadersFilter.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/SecurityHeadersFilter.java @@ -12,6 +12,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; +import java.util.Map; public class SecurityHeadersFilter implements Filter { @@ -47,17 +48,22 @@ private static void generateNonceHash() throws NoSuchAlgorithmException { } private void addResponseHeaders(HttpServletResponse resp) { + getConfiguredHeaders().entrySet().forEach(h -> resp.setHeader(h.getKey(), h.getValue())); + } + + public static Map getConfiguredHeaders(){ Config headers = LightbendConfigLoader.get().getConfig("angular.headers"); - resp.setHeader("X-XSS-Protection", headers.getString("X-XSS-Protection")); - resp.setHeader("X-Frame-Options", headers.getString("X-Frame-Options")); Config securityConfigs = headers.getConfig("Content-Security-Policy"); StringBuilder joined = new StringBuilder(); + String ngCspNonceVal = ngCspNonce.get() == null ? "" : ngCspNonce.get(); securityConfigs.entrySet().forEach((e) -> joined.append(e.getKey()).append(" ").append( - e.getValue().unwrapped().toString().replace("{{ngCspNonce}}", ngCspNonce.get()) + e.getValue().unwrapped().toString().replace("{{ngCspNonce}}", ngCspNonceVal) ).append("; ") ); - resp.setHeader("Content-Security-Policy", joined.toString()); + return Map.of("X-XSS-Protection",headers.getString("X-XSS-Protection"), + "X-Frame-Options",headers.getString("X-Frame-Options"), + "Content-Security-Policy",joined.toString()); } @Override diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/authentication/ShibbolethServlet.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/authentication/ShibbolethServlet.java index a900cacb17..c3f9cad994 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/authentication/ShibbolethServlet.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/authentication/ShibbolethServlet.java @@ -31,6 +31,8 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHeaders; import org.apache.log4j.Logger; import org.edu_sharing.repository.client.tools.CCConstants; @@ -41,6 +43,7 @@ import org.edu_sharing.repository.server.tools.ApplicationInfoList; import org.edu_sharing.repository.server.tools.security.ShibbolethSessions; import org.edu_sharing.repository.server.tools.security.ShibbolethSessions.SessionInfo; +import org.edu_sharing.restservices.lti.v13.ApiTool; import org.edu_sharing.service.authentication.AuthenticationExceptionMessages; import org.edu_sharing.service.authentication.EduAuthentication; import org.edu_sharing.service.authentication.SSOAuthorityMapper; @@ -50,6 +53,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.extensions.surf.util.URLDecoder; +import org.springframework.extensions.surf.util.URLEncoder; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; @@ -58,6 +62,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.util.*; import java.util.stream.Collectors; @@ -248,17 +253,23 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se redirect(resp,req); } catch(org.alfresco.repo.security.authentication.AuthenticationException e) { - if(e.getMessage() != null - && e.getMessage().contains(AuthenticationExceptionMessages.USER_BLOCKED)){ - logger.error(e.getMessage()); - resp.sendError(HttpServletResponse.SC_FORBIDDEN, AuthenticationExceptionMessages.USER_BLOCKED); - return; - }else{ - logger.error("INVALID ACCESS!",e); - resp.getOutputStream().println("INVALID ACCESS! "+e.getMessage()); - return; - } + processError(req, resp, e); + } + } + + private static void processError(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException { + String message = e.getMsgId(); + logger.error("shibboleth process error:" + message); + if(StringUtils.isEmpty(message)){ + message = "SSO_UNKNOWN_ERROR"; } + message = URLEncoder.encode(message.trim()); + resp.sendRedirect( + req.getScheme() +"://" + + req.getServerName() + +":"+ req.getServerPort() + + "/edu-sharing/components/error/"+message+"/"+message + ); } private void redirect(HttpServletResponse resp, HttpServletRequest req) throws IOException{ diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/connector/ConnectorServlet.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/connector/ConnectorServlet.java index c407c39f37..6e4105c0a6 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/connector/ConnectorServlet.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/connector/ConnectorServlet.java @@ -157,7 +157,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se jsonObject.put("endpoint",connector.getUrl()); jsonObject.put("tool", connector.getConnectorId()!=null ? connector.getConnectorId() : connector.getId()); jsonObject.put("defaultCreateElement", connector.getDefaultCreateElement()); - String mimetype = MimeTypesV2.getMimeType(properties, null); + String mimetype = MimeTypesV2.getMimeType(properties, NodeServiceHelper.getType(nodeRef)); jsonObject.put("mimetype",mimetype); for(ConnectorFileType filetype : connector.getFiletypes()){ if(filetype.getMimetype().equals(mimetype)) diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/AbstractJobMapAnnotationParams.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/AbstractJobMapAnnotationParams.java index cdb53614b2..04505dd9f2 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/AbstractJobMapAnnotationParams.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/AbstractJobMapAnnotationParams.java @@ -31,9 +31,13 @@ public final void execute(JobExecutionContext jobExecutionContext) throws JobExe if (field.getAnnotation(JobFieldDescription.class).file()) { field.set(this, jobExecutionContext.getJobDetail().getJobDataMap().get(JobHandler.FILE_DATA)); } else if (field.getType().isEnum()) { - field.set(this, mapEnum(field.getType(), jobExecutionContext.getJobDetail().getJobDataMap().getString(field.getName()))); + if(value != null) { + field.set(this, mapEnum(field.getType(), jobExecutionContext.getJobDetail().getJobDataMap().getString(field.getName()))); + } } else if (field.getType().isPrimitive()) { - field.set(this, value); + if(value != null) { + field.set(this, value); + } } else if (field.getType().isAssignableFrom(Collection.class) || field.getType().equals(List.class)) { if (value != null) { Type abstractType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/FixOrganisationAdminGroup.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/FixOrganisationAdminGroup.java index 723d3cb692..c67f89dca5 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/FixOrganisationAdminGroup.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/FixOrganisationAdminGroup.java @@ -52,7 +52,7 @@ public void execute(JobExecutionContext jobExecutionContext) throws JobExecution Set allGroups = authorityService.getAllAuthoritiesInZone(AuthorityService.ZONE_APP_DEFAULT, AuthorityType.GROUP); Set allOrganisations = allGroups.stream() .filter(g -> - nodeService.hasAspect(authorityService.getAuthorityNodeRef(g), OrganisationService.QNAME_EDUGROUP) + nodeService.hasAspect(authorityService.getAuthorityNodeRef(g), OrganisationService.ASPECT_EDUGROUP) && (organisationFilter.size() == 0 || organisationFilter.contains(g)) ) .collect(Collectors.toSet()); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/SetPermissionsOrgAdminGroup.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/SetPermissionsOrgAdminGroup.java index d8152d76d0..566185f76b 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/SetPermissionsOrgAdminGroup.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/jobs/quartz/SetPermissionsOrgAdminGroup.java @@ -2,13 +2,14 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.stream.Collectors; +import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.apache.log4j.Logger; import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.context.ApplicationContext; @@ -45,7 +46,7 @@ public Void doWork() throws Exception { private void run(String[] orgs, boolean execute) { if(orgs == null) { - orgs = EduGroupCache.getNames(); + orgs = organisationService.getOrganisations().stream().map(o -> o.get(ContentModel.PROP_AUTHORITY_NAME)).collect(Collectors.toList()).toArray(new String[0]); } logger.info("running for " + orgs.length + " orgs"); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/oai/OaiServlet.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/oai/OaiServlet.java index 5c480bc2fd..e00e520c83 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/oai/OaiServlet.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/oai/OaiServlet.java @@ -52,6 +52,10 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { Config config = LightbendConfigLoader.get().getConfig("exporter.oai.identify"); + if(!LightbendConfigLoader.get().getBoolean("exporter.oai.enabled")) { + resp.setStatus(HttpServletResponse.SC_FORBIDDEN); + return; + } int itemsPerPage = LightbendConfigLoader.get().getInt("exporter.oai.itemsPerPage"); //oai/provider?verb=GetRecord&metadataPrefix=lom&identifier=3410648a-465e-47ff-87fe-706b89cecd65 RepositoryConfiguration configuration = new RepositoryConfiguration(). diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/EduGroupTool.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/EduGroupTool.java deleted file mode 100644 index ce86ff03ef..0000000000 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/EduGroupTool.java +++ /dev/null @@ -1,454 +0,0 @@ -/** - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ -package org.edu_sharing.repository.server.tools; - -import java.io.Serializable; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.namespace.QName; -import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; -import org.edu_sharing.repository.client.tools.CCConstants; -import org.springframework.context.ApplicationContext; - -/** - * Tools for working the edu group aspect on normal alfresco user group. - * @author Christian - */ -public class EduGroupTool { - - public static final QName QNAME_EDUGROUP = QName.createQName(CCConstants.CCM_ASPECT_EDUGROUP ); - - public static final String TYPE_HOMEDIR = CCConstants.CCM_TYPE_MAP; - public static final QName QNAME_HOMEDIR = QName.createQName(TYPE_HOMEDIR); - - public static final String CCM_PROP_EDUGROUP_EDU_HOMEDIR = "{http://www.campuscontent.de/model/1.0}edu_homedir"; - public static final String CCM_PROP_EDUGROUP_EDU_UNIQUENAME = "{http://www.campuscontent.de/model/1.0}edu_uniquename"; - - private static final String NAME_SYSTEM = "{http://www.alfresco.org/model/system/1.0}system"; - private static final String NAME_AUTHORITIES = "{http://www.alfresco.org/model/system/1.0}authorities"; - - private static final String PROP_AUTHORITYNAME = "{http://www.alfresco.org/model/user/1.0}authorityName"; - - private static boolean doneInit = false; - private static NodeRef authorityRootNodeRef = null; - - private static long lastMapRefresh = 0; - - // Map [PROP_AUTHORITYNAME] --> [NODEREF OF EDUGROUP] - // Important: [NAME OF A GROUP] is unique on an Alfresco node - private static Map cachedGroupNameMap = null; - - // Map [NODEREF OF EDUGROUP (String)] --> [PARENT NODEREF OF EDUGROUP (String)] - private static Map cachedGroupParentMap = null; - - // use this object for synchronized code blocks that are working stateful on - // cachedGroupNameMap OR cachedGroupParentMap - public static Boolean syncAccessLock = new Boolean(true); - - /** - * Prepare to work with the authorities/groups stored in userstore - * @throws Exception - */ - private static synchronized void init()throws Exception { - - if (doneInit) return; - - // get services to work with - ApplicationContext applicationContext = AlfAppContextGate.getApplicationContext(); - ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); - NodeService nodeService = serviceRegistry.getNodeService(); - - // start in store root and get base node of all authorities - NodeRef userStoreRoot = nodeService.getRootNode(new StoreRef("user://alfrescoUserStore")); - NodeRef systemRoot = getNodeRefWithQName(nodeService.getChildAssocs(userStoreRoot), NAME_SYSTEM); - authorityRootNodeRef = getNodeRefWithQName(nodeService.getChildAssocs(systemRoot), NAME_AUTHORITIES); - - doneInit = true; - } - - /** - * Loads a cashed Map of all edugroups from userStore. - * Use if a lot requests are performed on Map and its no problem if Map is less than 20 secs old - * - * KEY -> Groupname - * VALUE -> NodeRef - */ - private static Map getCashedEduGroupMap() throws Exception { - - // check if map needs refresh - synchronized(syncAccessLock) { - long actualTime = new Date().getTime(); - if ((cachedGroupNameMap==null) || ((actualTime-(20*1000))>lastMapRefresh)) { - - // get services to work with - ApplicationContext applicationContext = AlfAppContextGate.getApplicationContext(); - ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); - NodeService nodeService = serviceRegistry.getNodeService(); - - cachedGroupNameMap = getEduGroupMap(nodeService); - cachedGroupParentMap = createCachedGroupParentMap(nodeService, cachedGroupNameMap); - lastMapRefresh = actualTime; - } - } - - return cachedGroupNameMap; - } - - /** - * Builds from the group map a parent map. - * @param nodeService - * @param groupMap - * @return - * @throws Exception - */ - private static Map createCachedGroupParentMap(NodeService nodeService, Map groupMap) throws Exception { - Map result = new HashMap<>(); - for (String groupName : groupMap.keySet()) { - NodeRef nodeRef = groupMap.get(groupName); - if (nodeService.getParentAssocs(nodeRef).size()==1) { - String parentNodeRef = nodeService.getParentAssocs(nodeRef).get(0).getParentRef().toString(); - result.put(nodeRef.toString(), parentNodeRef); - } else { - throw new Exception("Node '"+nodeRef.toString()+"' has '"+nodeService.getParentAssocs(nodeRef).size()+"' Parents !! not ==1 !!"); - } - } - return result; - } - - /** - * Loads a fresh Map of all edugroups from userStore. - * KEY -> Groupname - * VALUE -> NodeRef - */ - public static Map getEduGroupMap(final NodeService nodeService) throws Exception { - - AuthenticationUtil.RunAsWork> runAs = () -> { - if (!doneInit) init(); - return getEduGroupMapFromNodeContainer(nodeService, authorityRootNodeRef, 1); - }; - - return AuthenticationUtil.runAs(runAs, ApplicationInfoList.getHomeRepository().getUsername()); - } - - /** - * Checks if a given group is local, which means that the user is in that group or the group is - * a sub group of a group he is in. - * @param listOfGroups_NodeRefStr_UserIsIn All the groups the user is in / List of NodeRefs in String format - * @param group_NodeRefStr_UserIsIn The group to test in NodeRef String format - * @return - * @throws Exception - */ - public static boolean isEduGroupLocal(Set listOfGroups_NodeRefStr_UserIsIn, String group_NodeRefStr_UserIsIn) throws Exception { - - if (!doneInit) init(); - - if (cachedGroupParentMap==null) { - System.err.println("WARNING: isEduGroupLocal() called on EduGroupTools but cachedGroupParentMap is NULL (no init done)"); - return false; - } - - String actualNodeRefStr = group_NodeRefStr_UserIsIn; - while (actualNodeRefStr!=null) { - if (listOfGroups_NodeRefStr_UserIsIn.contains(actualNodeRefStr)) return true; - actualNodeRefStr = cachedGroupParentMap.get(actualNodeRefStr); - } - - return false; - } - - /** - * Returns the path of eduGroups the actual eduGroup is a sub group of - * @param group_NodeRefStr_UserIsIn - * @return - */ - public static String getParentPathForEduGroup(String group_NodeRefStr_UserIsIn) { - String parentPath = "/"; - String parentNodeRef = cachedGroupParentMap.get(group_NodeRefStr_UserIsIn); - while (parentNodeRef!=null) { - // if parent is a eduGroup - if (cachedGroupNameMap.containsValue(new NodeRef(parentNodeRef))) { - // get name from reverse lookup of groupNameMap - String groupName = null; - synchronized(syncAccessLock) { - Iterator keys = cachedGroupNameMap.keySet().iterator(); - while ((groupName==null) && (keys.hasNext())) { - String name = keys.next(); - NodeRef toTest = cachedGroupNameMap.get(name); - if ((toTest!=null) && (toTest.toString().equals(parentNodeRef))) { - if (name.startsWith("GROUP_")) name = name.substring(6); - groupName = name; - } - } - } - if (groupName!=null) parentPath = parentPath+groupName+"/"; - } - // get next parent - parentNodeRef = cachedGroupParentMap.get(parentNodeRef); - } - return parentPath; - } - - /* - * Recursive traversal of userstore ... - */ - private static Map getEduGroupMapFromNodeContainer(NodeService nodeService, NodeRef node, int recDepth) { - - Map results = new HashMap<>(); - - // to prevent possible infintiv loops in a node graph ... stop at a recursive level of 5 - if (recDepth>5) return results; - - // check current node for a edugroup - if (nodeService.hasAspect(node, QNAME_EDUGROUP)) { - String groupName = (String)nodeService.getProperty(node, QName.createQName(PROP_AUTHORITYNAME)); - if (groupName!=null) { - results.put(groupName, node); - } - } - - // check children recursive - List childs = nodeService.getChildAssocs(node); - for (ChildAssociationRef childAssociationRef : childs) { - results.putAll(getEduGroupMapFromNodeContainer(nodeService, childAssociationRef.getChildRef(), (recDepth+1))); - } - - return results; - } - - public static NodeRef getNodeRefWithQName(List childs, String qNameStr) throws Exception { - - if ((childs==null) || (childs.size()==0)) throw new Exception("list of childs is empty ... cannot find child with qName '"+qNameStr+"'"); - - for (ChildAssociationRef childAssociationRef : childs) { - if (childAssociationRef.getQName().isMatch(QName.createQName(qNameStr))) return childAssociationRef.getChildRef(); - } - - throw new Exception("cannot find child with QName '"+qNameStr+"' in userstore"); - } - - /** - * Returns all NodeRefs (String format) of edu groups a user is assigned to - * @param username - * @return - */ - public static Set getAllEduGroupsOfUserAsNodeRefStrings(String username) throws Exception { - - Set preresults = getAllEduGroupsOfUserAsName(username); - Set results = new HashSet(preresults.size()); - for (String string : preresults) { - results.add(getNodeRefFromEduGroupUniqueName(string)); - } - return results; - } - - /** - * Returns all GroupNames (alfresco identifier) of edu groups a user is assigned to - * @param username - * @return - */ - public static Set getAllEduGroupsOfUserAsName(String username) throws Exception { - - Set resultList = new HashSet(); - - // get services to work with - ApplicationContext applicationContext = AlfAppContextGate.getApplicationContext(); - ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); - - // all alfresco groups the user is in - Set authorityList = null; - try { - authorityList = serviceRegistry.getAuthorityService().getContainingAuthorities(AuthorityType.GROUP, username, false); - } catch (Exception e) { - e.printStackTrace(); - } - - if (authorityList==null) { - System.err.println("[ERROR] Not able to get groups for username '"+username+"' ... check if Exception above"); - return resultList; - } - - // filter out just the edu groups - for (String groupname : authorityList) { - if (getCashedEduGroupMap().containsKey(groupname)) { - resultList.add(groupname); - } - } - - return resultList; - - } - - public static String getNodeRefFromEduGroupUniqueName(String groupName) throws Exception { - - if (!doneInit) init(); - NodeRef nodeRef = getCashedEduGroupMap().get(groupName); - - if (nodeRef!=null) { - return nodeRef.toString(); - } else { - return null; - } - - } - - /** - * Returns the username of the actual context - * @return - */ - public static String getTheCurrentUsername() { - - // get services to work with - ApplicationContext applicationContext = AlfAppContextGate.getApplicationContext(); - ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); - - // get the current username - return serviceRegistry.getAuthenticationService().getCurrentUserName(); - } - - /** - * Checks if two sets are containing the same string item (group node ref) - * @param one - * @param two - * @return - */ - public static boolean gotAtleastOneSameGroupInSets(Set one, Set two) { - for (String itemInListOne : one) { - if (two.contains(itemInListOne)) return true; - } - return false; - } - - /** - * Processing Mini Command Line for adding and deleting the EduGroup Aspect to or from an Alfresco group - * - * Attention: expects that the current thread is authenticated - * @param propName - * @return (can be ignored) - * @throws Exception - */ - public static void processEduGroupMicroCommand(String propName) throws Exception { - - String syntaxHelp = "Use Syntax: COMMAND [ADD|REMOVE] [GROUPNODEREF] [GROUPHOMEFOLDERNODEREF(just needed on ADD command)]"; - - // get services to work with - ApplicationContext applicationContext = AlfAppContextGate.getApplicationContext(); - ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); - NodeService nodeService = serviceRegistry.getNodeService(); - - // parse command line - String commandLine = ""; - if (propName.startsWith("COMMAND ")) { - commandLine = propName.substring(8); - } - - // parse command ... - String groupNode = ""; - String homeNode = ""; - - // ADD ASPECT TO GROUP - if (commandLine.startsWith("ADD")) { - - commandLine = commandLine.substring(3).trim(); - - // get groupnode param - int spaceIndex = commandLine.indexOf(' '); - if (spaceIndex<=0) throw new Exception(syntaxHelp); - groupNode = commandLine.substring(0,spaceIndex).trim(); - commandLine = commandLine.substring(spaceIndex).trim(); - - //alfresco34E - //if ((!NodeRef.isNodeRef(groupNode)) || (!groupNode.startsWith("user"))) throw new Exception("Group Node Refrence needs to be like this format 'user://alfrescoUserStore/1c64cabc-97bd-4628-86a8-4860cdc73342' and NOT like '"+groupNode+"'"); - - if ((!NodeRef.isNodeRef(groupNode)) || (!groupNode.startsWith("workspace"))) throw new Exception("Group Node Refrence needs to be like this format 'workspace://SpacesStore/1c64cabc-97bd-4628-86a8-4860cdc73342' and NOT like '"+groupNode+"'"); - - if(!nodeService.getType(new NodeRef(groupNode)).equals(QName.createQName(CCConstants.CM_TYPE_AUTHORITY_CONTAINER))){ - throw new Exception(groupNode+" is no Group"); - } - - // get homenode param - if (commandLine.length()<=0) throw new Exception(syntaxHelp); - homeNode = commandLine; - if (!NodeRef.isNodeRef(homeNode)) throw new Exception("Home Node Refrence needs to be like this format 'workspace://SpacesStore/93303a1f-187e-4f49-9fef-7b80e982da2c' and NOT like '"+homeNode+"'"); - - if( !(nodeService.getType(new NodeRef(homeNode)).equals(QName.createQName(CCConstants.CM_TYPE_FOLDER)) || nodeService.getType(new NodeRef(homeNode)).equals(QName.createQName(CCConstants.CCM_TYPE_MAP)) )){ - throw new Exception(homeNode+" is no Folder"); - } - - NodeRef groupRef = new NodeRef(groupNode); - NodeRef homeRef = new NodeRef(homeNode); - - String linkName = (String) nodeService.getProperty( - groupRef - , QName.createQName(CCConstants.CM_PROP_AUTHORITY_AUTHORITYNAME)); - - // typ must be set, because needed for request - nodeService.setType(homeRef, QNAME_HOMEDIR); - - // add aspect to group - Map params = new HashMap<>(); - params.put(QName.createQName(CCM_PROP_EDUGROUP_EDU_HOMEDIR), homeRef); - params.put(QName.createQName(CCM_PROP_EDUGROUP_EDU_UNIQUENAME), linkName); - nodeService.addAspect(groupRef, QNAME_EDUGROUP, params); - - } else if (commandLine.startsWith("REMOVE")) { - - commandLine = commandLine.substring(6).trim(); - - // get groupnode param - if (commandLine.length()<=0) throw new Exception(syntaxHelp); - int spaceIndex = commandLine.indexOf(' '); - if (spaceIndex<=0) spaceIndex = commandLine.length(); - groupNode = commandLine.substring(0,spaceIndex).trim(); - - if(!nodeService.getType(new NodeRef(groupNode)).equals(QName.createQName(CCConstants.CM_TYPE_AUTHORITY_CONTAINER))){ - throw new Exception(groupNode+" is no Group"); - } - - // remove aspect - nodeService.removeAspect(new NodeRef(groupNode), QNAME_EDUGROUP); - - } else { - throw new Exception(syntaxHelp); - } - - } - -} diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/InitHelper.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/InitHelper.java index 89f901c6bc..8ab07b1fb0 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/InitHelper.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/server/tools/InitHelper.java @@ -1,29 +1,38 @@ package org.edu_sharing.repository.server.tools; import com.typesafe.config.Config; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; import org.apache.log4j.Logger; import org.edu_sharing.alfresco.lightbend.LightbendConfigLoader; +import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.service.authority.AuthorityService; import org.edu_sharing.service.authority.AuthorityServiceFactory; import org.edu_sharing.service.authority.AuthorityServiceImpl; -import org.edu_sharing.service.nodeservice.NodeServiceImpl; -import java.util.List; +import java.io.Serializable; +import java.util.*; public class InitHelper { static Logger logger = Logger.getLogger(InitHelper.class); + public static void initGroups() throws Exception { List createGroups = LightbendConfigLoader.get().getConfigList("repository.groups.create"); - if(createGroups != null && !createGroups.isEmpty()) { + if (createGroups != null && !createGroups.isEmpty()) { AuthorityService authorityService = AuthorityServiceFactory.getLocalService(); - for(Config group: createGroups) { + for (Config group : createGroups) { String id = group.getString("id"); - if(!id.startsWith(PermissionService.GROUP_PREFIX)) { + if (!id.startsWith(PermissionService.GROUP_PREFIX)) { id = PermissionService.GROUP_PREFIX + id; } - if(authorityService.getAuthorityNodeRef(id) == null) { + if (authorityService.getAuthorityNodeRef(id) == null) { logger.info("Init group " + id); authorityService.createGroup(id, group.getString("displayName"), null); } @@ -31,8 +40,85 @@ public static void initGroups() throws Exception { } } - public static void initProxyUser(){ + private static final List userProps = List.of( + ContentModel.PROP_USERNAME, + ContentModel.PROP_HOMEFOLDER, + ContentModel.PROP_FIRSTNAME, + ContentModel.PROP_LASTNAME, + ContentModel.PROP_EMAIL, + ContentModel.PROP_ORGID, + ContentModel.PROP_HOME_FOLDER_PROVIDER, + ContentModel.PROP_DEFAULT_HOME_FOLDER_PATH, + ContentModel.PROP_PRESENCEPROVIDER, + ContentModel.PROP_PRESENCEUSERNAME, + ContentModel.PROP_ORGANIZATION, + ContentModel.PROP_JOBTITLE, + ContentModel.PROP_LOCATION, + ContentModel.PROP_PERSONDESC, + ContentModel.PROP_TELEPHONE, + ContentModel.PROP_MOBILE, + ContentModel.PROP_COMPANYADDRESS1, + ContentModel.PROP_COMPANYADDRESS2, + ContentModel.PROP_COMPANYADDRESS3, + ContentModel.PROP_COMPANYPOSTCODE, + ContentModel.PROP_COMPANYTELEPHONE, + ContentModel.PROP_COMPANYFAX, + ContentModel.PROP_COMPANYEMAIL, + ContentModel.PROP_SKYPE, + ContentModel.PROP_GOOGLEUSERNAME, + ContentModel.PROP_INSTANTMSG, + ContentModel.PROP_USER_STATUS, + ContentModel.PROP_USER_STATUS_TIME + ); + + public static void initPersons() { + ServiceRegistry serviceRegistry = AlfAppContextGate.getApplicationContext().getBean(ServiceRegistry.class); + RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper(); + PersonService personService = serviceRegistry.getPersonService(); + MutableAuthenticationService authenticationService = serviceRegistry.getAuthenticationService(); + org.alfresco.service.cmr.security.AuthorityService authorityService = serviceRegistry.getAuthorityService(); + + List createPersons = LightbendConfigLoader.get().getConfigList("repository.persons"); + if (createPersons != null && !createPersons.isEmpty()) { + for (Config person : createPersons) { + retryingTransactionHelper.doInTransaction(() -> { + String authorityName = person.getString("authorityName"); + char[] password = person.getString("password").toCharArray(); + Config profile = person.getConfig("profile"); + NodeRef guestRef = personService.getPersonOrNull(authorityName); + if (guestRef != null) { + return null; + } + + Map properties = new HashMap<>(Map.of( + ContentModel.PROP_USERNAME, authorityName, + QName.createQName(CCConstants.CM_PROP_PERSON_EDU_SCHOOL_PRIMARY_AFFILIATION), person.getString("primaryAffiliation")) + ); + + for (QName property : userProps) { + if (profile.hasPath(property.getLocalName())) { + String value = profile.getString(property.getLocalName()); + properties.put(property, value); + } + } + + authenticationService.createAuthentication(authorityName, password); + personService.createPerson(properties); + Set currentMemberships = new HashSet<>(authorityService.getAuthoritiesForUser(authorityName)); + + Set toCreate = new HashSet<>(person.getStringList("groups")); + toCreate.remove(CCConstants.AUTHORITY_GROUP_EVERYONE); + toCreate.removeAll(currentMemberships); + + toCreate.forEach(x -> authorityService.addAuthority(x, authorityName)); + return null; + }); + } + } + } + + public static void initProxyUser() { //init proxyuser - ((AuthorityServiceImpl)AuthorityServiceFactory.getLocalService()).createProxyUser(); + ((AuthorityServiceImpl) AuthorityServiceFactory.getLocalService()).createProxyUser(); } } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_3_2_FillOriginalId.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_3_2_FillOriginalId.java index 80f3e4327d..7ad3f538c4 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_3_2_FillOriginalId.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_3_2_FillOriginalId.java @@ -9,16 +9,18 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; +import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.server.MCAlfrescoAPIClient; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; import org.edu_sharing.repository.server.update.UpdateRoutine; import org.edu_sharing.repository.server.update.UpdateService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Map; @Slf4j @UpdateService @@ -31,18 +33,21 @@ public class Release_3_2_FillOriginalId { private final BehaviourFilter policyBehaviourFilter; + OrganisationService organisationService; + //very important: use the alfrescoDefaultDbNodeService defined in custom-core-services-context.xml //cause of overwriten getChild... methods in org.edu_sharing.alfresco.fixes.DbNodeServiceImpl //this can lead to a problem, that every edugroupfolder is processed for all members of the edugroup again @Autowired - public Release_3_2_FillOriginalId(RetryingTransactionHelper retryingTransactionHelper, @Qualifier("alfrescoDefaultDbNodeService") NodeService nodeService, @Qualifier("policyBehaviourFilter")BehaviourFilter policyBehaviourFilter ) { + public Release_3_2_FillOriginalId(RetryingTransactionHelper retryingTransactionHelper, @Qualifier("alfrescoDefaultDbNodeService") NodeService nodeService, @Qualifier("policyBehaviourFilter")BehaviourFilter policyBehaviourFilter, OrganisationService organisationService ) { this.retryingTransactionHelper = retryingTransactionHelper; this.nodeService = nodeService; this.policyBehaviourFilter = policyBehaviourFilter; + this.organisationService = organisationService; try{ - for(NodeRef nodeRef : EduGroupCache.getKeys()){ - NodeRef eduGroupFolderNodeId = (NodeRef)nodeService.getProperty(nodeRef, QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR)); + for(Map orgProps : organisationService.getOrganisations()){ + NodeRef eduGroupFolderNodeId = (NodeRef)orgProps.get(QName.createQName(CCConstants.CCM_PROP_EDUGROUP_EDU_HOMEDIR)); eduGroupFolderNodeIds.add(eduGroupFolderNodeId); } }catch(Throwable e){ diff --git a/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_9_1_EduGroupFolder.java b/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_9_1_EduGroupFolder.java new file mode 100644 index 0000000000..5ab956d810 --- /dev/null +++ b/Backend/services/core/src/main/java/org/edu_sharing/repository/update/Release_9_1_EduGroupFolder.java @@ -0,0 +1,48 @@ +package org.edu_sharing.repository.update; + + +import lombok.extern.slf4j.Slf4j; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.edu_sharing.alfresco.service.OrganisationService; +import org.edu_sharing.repository.server.update.UpdateRoutine; +import org.edu_sharing.repository.server.update.UpdateService; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +@Slf4j +@UpdateService +public class Release_9_1_EduGroupFolder { + + private final OrganisationService organisationService; + + + @Autowired + public Release_9_1_EduGroupFolder(OrganisationService organisationService){ + this.organisationService = organisationService; + } + + @UpdateRoutine( + id = "Release_9_1_EduGroupFolder", + description = "add ccm:edugroup_folder aspect to the organisation folders", + order = 0, + auto = true) + public void execute() { + List> organisations = organisationService.getOrganisations(); + for(Map orgProps : organisations){ + NodeRef homeFolderRef = (NodeRef)orgProps.get(OrganisationService.PROP_EDUGROUP_EDU_HOMEDIR); + String authorityName = (String)orgProps.get(ContentModel.PROP_AUTHORITY_NAME); + try { + organisationService.bindEduGroupToFolder(authorityName, homeFolderRef); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + } + + } +} diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/ApiAuthenticationFilter.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/ApiAuthenticationFilter.java index c384277262..e63257d063 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/ApiAuthenticationFilter.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/ApiAuthenticationFilter.java @@ -4,6 +4,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; +import com.typesafe.config.Config; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletException; @@ -16,6 +17,7 @@ import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.apache.log4j.Logger; import org.edu_sharing.alfresco.authentication.subsystems.SubsystemChainingAuthenticationService; +import org.edu_sharing.alfresco.lightbend.LightbendConfigLoader; import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.server.AuthenticationToolAPI; @@ -25,6 +27,7 @@ import org.edu_sharing.service.authentication.oauth2.TokenService; import org.edu_sharing.service.authentication.oauth2.TokenService.Token; import org.edu_sharing.service.authority.AuthorityServiceFactory; +import org.edu_sharing.service.authority.AuthorityServiceHelper; import org.edu_sharing.service.config.ConfigServiceFactory; import org.edu_sharing.service.toolpermission.ToolPermissionServiceFactory; import org.edu_sharing.spring.security.basic.CSRFConfig; @@ -115,7 +118,7 @@ public void doFilter(ServletRequest req, ServletResponse resp, } } - + Config accessConfig = LightbendConfigLoader.get().getConfig("security.access"); List AUTHLESS_ENDPOINTS = Arrays.asList(new String[]{"/authentication", "/_about", "/config", "/register", "/sharing", "/lti/v13/oidc/login_initiations", "/lti/v13/lti13", @@ -142,8 +145,8 @@ public void doFilter(ServletRequest req, ServletResponse resp, } boolean noAuthenticationNeeded = false; + String pathInfo = httpReq.getPathInfo(); for (String endpoint : AUTHLESS_ENDPOINTS) { - String pathInfo = httpReq.getPathInfo(); if (pathInfo == null) { continue; } @@ -155,7 +158,6 @@ public void doFilter(ServletRequest req, ServletResponse resp, } boolean adminRequired = false; for (String endpoint : ADMIN_ENDPOINTS) { - String pathInfo = httpReq.getPathInfo(); if (pathInfo == null) { continue; } @@ -167,19 +169,33 @@ public void doFilter(ServletRequest req, ServletResponse resp, } for (String endpoint : DISABLED_ENDPOINTS) { - String pathInfo = httpReq.getPathInfo(); if (pathInfo == null) { continue; } if (pathInfo.startsWith(endpoint)) { - httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpResp.setStatus(HttpServletResponse.SC_FORBIDDEN); httpResp.flushBuffer(); httpResp.getWriter().print("This endpoint is disabled via config"); return; } } + for (Map.Entry endpoint : accessConfig.getObject("endpoints").unwrapped().entrySet()) { + if (pathInfo == null) { + continue; + } + if (pathInfo.startsWith(endpoint.getKey()) && ( + endpoint.getValue().toString().equalsIgnoreCase("admin") && !AuthorityServiceHelper.isAdmin() + || endpoint.getValue().toString().equalsIgnoreCase("disabled") + )) { + httpResp.setStatus(HttpServletResponse.SC_FORBIDDEN); + httpResp.getWriter().print("This endpoint is disabled via config"); + httpResp.flushBuffer(); + return; + } + } + if (adminRequired && !AuthorityServiceFactory.getLocalService().isGlobalAdmin()) { httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResp.flushBuffer(); @@ -197,14 +213,22 @@ public void doFilter(ServletRequest req, ServletResponse resp, // ignore the auth for the login if (validatedAuth == null && (!noAuthenticationNeeded && !trustedAuth)) { - String pathInfo = httpReq.getPathInfo(); - if (pathInfo != null && pathInfo.equals("/openapi.json")) + if (pathInfo != null && pathInfo.equals("/openapi.json")) { httpResp.setHeader("WWW-Authenticate", "BASIC realm=\"" + "Edu-Sharing Rest API" + "\""); + } httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResp.flushBuffer(); return; } - + if (pathInfo != null && pathInfo.equals("/openapi.json")) { + String openApiAccess = accessConfig.getString("openapi"); + if (openApiAccess.equalsIgnoreCase("admin") && !AuthorityServiceHelper.isAdmin() || openApiAccess.equalsIgnoreCase("disabled")) { + httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpResp.flushBuffer(); + httpResp.getWriter().print("This endpoint is disabled via config"); + return; + } + } // Chain other filters chain.doFilter(req, resp); } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/CollectionDao.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/CollectionDao.java index 43926795cc..798495097e 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/CollectionDao.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/CollectionDao.java @@ -3,6 +3,7 @@ import java.io.InputStream; import java.util.*; +import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.security.PermissionService; @@ -10,6 +11,7 @@ import org.edu_sharing.alfresco.service.toolpermission.ToolPermissionException; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.server.MCAlfrescoBaseClient; +import org.edu_sharing.repository.server.SearchResultNodeRef; import org.edu_sharing.restservices.collection.v1.model.*; import org.edu_sharing.restservices.collection.v1.model.Collection; import org.edu_sharing.restservices.node.v1.model.AbstractEntries; @@ -19,6 +21,9 @@ import org.edu_sharing.service.collection.CollectionService; import org.edu_sharing.service.collection.CollectionServiceFactory; import org.edu_sharing.service.nodeservice.NodeServiceHelper; +import org.edu_sharing.service.search.SearchService; +import org.edu_sharing.service.search.SearchServiceElastic; +import org.edu_sharing.service.search.SearchServiceFactory; import org.edu_sharing.service.search.model.SortDefinition; import org.edu_sharing.service.toolpermission.ToolPermissionServiceFactory; @@ -131,53 +136,75 @@ public static CollectionBaseEntries getCollectionsSubcollections(RepositoryDao r } private static CollectionBaseEntries getCollectionsChildren(RepositoryDao repoDao, String parentId, SearchScope scope, boolean fetchCounts, Filter propFilter, List filter, SortDefinition sortDefinition, int skipCount, int maxItems) throws DAOException { try { - List result = new ArrayList<>(); - NodeDao parentNode = null; + NodeDao parentNode; if(!ROOT.equals(parentId)) { parentNode = NodeDao.getNode(repoDao, parentId); parentNode.fetchCounts = false; } - List children = - repoDao.getCollectionClient().getChildren( - ROOT.equals(parentId) ? null : parentId, - scope != null ? scope.toString() : null, sortDefinition, filter); - - - //NodeDao.convertAlfrescoNodeRef(repoDao,children) - NodeEntries sorted = NodeDao.convertToRest(repoDao, - Filter.createShowAllFilter(), - NodeDao.convertAlfrescoNodeRef(repoDao, children), - skipCount, - maxItems, - (dao) -> { - dao.fetchCounts = fetchCounts; - return dao; - }); - Pagination pagination = sorted.getPagination(); - for (Node child : sorted.getNodes()) { - - String nodeType = child.getType(); - - if (CCConstants.getValidLocalName(CCConstants.CCM_TYPE_MAP).equals(nodeType)) { - - // it's a collection - result.add(child); - - } else if (CCConstants.getValidLocalName(CCConstants.CCM_TYPE_IO).equals(nodeType)) { - - // it's a reference - try { - result.add((CollectionReference) child); - }catch(ClassCastException e) { - logger.error("Collection "+parentId+" contains a non-ref object: "+child.getRef().getId()+". Please clean up the collection", e); + if(!ROOT.equals(parentId) || SearchScope.RECENT.equals(scope)) { + List result = new ArrayList<>(); + List children; + if(SearchScope.RECENT.equals(scope)) { + children = repoDao.getCollectionClient().getRecentForCurrentUser(); + } else { + children = + repoDao.getCollectionClient().getChildren( + ROOT.equals(parentId) ? null : parentId, + scope != null ? scope.toString() : null, sortDefinition, filter); } + + //NodeDao.convertAlfrescoNodeRef(repoDao,children) + NodeEntries sorted = NodeDao.convertToRest(repoDao, + Filter.createShowAllFilter(), + NodeDao.convertEduNodeRef(repoDao, children), + skipCount, + maxItems, + (dao) -> { + dao.fetchCounts = fetchCounts; + return dao; + }); + Pagination pagination = sorted.getPagination(); + for (Node child : sorted.getNodes()) { + + String nodeType = child.getType(); + + if (CCConstants.getValidLocalName(CCConstants.CCM_TYPE_MAP).equals(nodeType)) { + + // it's a collection + result.add(child); + + } else if (CCConstants.getValidLocalName(CCConstants.CCM_TYPE_IO).equals(nodeType)) { + + // it's a reference + try { + result.add((CollectionReference) child); + } catch (ClassCastException e) { + logger.error("Collection " + parentId + " contains a non-ref object: " + child.getRef().getId() + ". Please clean up the collection", e); + } + } + } + CollectionBaseEntries obj = new CollectionBaseEntries(); + obj.setEntries(result); + obj.setPagination(pagination); + return obj; + + } else { + SearchResultNodeRef searchResult = repoDao.getCollectionClient().getRoot( + scope != null ? scope.toString() : null, sortDefinition, skipCount, maxItems + ); + BoolQuery readPermissionsQuery = null; + SearchService searchService = SearchServiceFactory.getSearchService(repoDao.getId()); + if(searchService instanceof SearchServiceElastic) { + // improve performance by caching the relatively expensive query + readPermissionsQuery = ((SearchServiceElastic)searchService).getReadPermissionsQuery(new BoolQuery.Builder()).build(); } + NodeSearch transformed = NodeDao.transform(repoDao, searchResult, propFilter, null, readPermissionsQuery); + CollectionBaseEntries obj = new CollectionBaseEntries(); + obj.setEntries(transformed.getNodes()); + obj.setPagination(new Pagination(searchResult)); + return obj; } - CollectionBaseEntries obj = new CollectionBaseEntries(); - obj.setEntries(result); - obj.setPagination(pagination); - return obj; - }catch(Exception e){ + }catch(Throwable e){ throw DAOException.mapping(e); } } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/NodeDao.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/NodeDao.java index 369daf7ffd..fd9293c57e 100755 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/NodeDao.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/NodeDao.java @@ -360,7 +360,7 @@ public static NodeSearch search(RepositoryDao repoDao, MdsDao mdsDao, BoolQuery readPermissionsQuery = null; if(searchService instanceof SearchServiceElastic) { // improve performance by caching the relatively expensive query - readPermissionsQuery = ((SearchServiceElastic)SearchServiceFactory.getSearchService(repoDao.getId())).getReadPermissionsQuery(new BoolQuery.Builder()).build(); + readPermissionsQuery = ((SearchServiceElastic)searchService).getReadPermissionsQuery(new BoolQuery.Builder()).build(); } NodeSearch result = transform(repoDao, searchService.search(mdsDao.getMds(), query, criteriasMap, token), filter, transform, readPermissionsQuery); if (result.getCount() == 0) { @@ -2074,15 +2074,21 @@ private static String getVersionLabel(int major, int minor) { private NodeVersionRef transformVersion(String versionLabel) { - String[] versionTokens = versionLabel.split("\\."); - - NodeVersionRef version = new NodeVersionRef(); - version.setNode(getRef()); - version.setMajor(Integer.parseInt(versionTokens[0])); - version.setMinor(Integer.parseInt(versionTokens[1])); - version.setMinor(Integer.parseInt(versionTokens[1])); + try { + String[] versionTokens = versionLabel.split("\\."); - return version; + NodeVersionRef version = new NodeVersionRef(); + version.setNode(getRef()); + version.setMajor(Integer.parseInt(versionTokens[0])); + version.setMinor(Integer.parseInt(versionTokens[1])); + version.setMinor(Integer.parseInt(versionTokens[1])); + return version; + } catch(Throwable e) { + logger.warn("Could not parse version for node " + nodeId, e); + NodeVersionRef version = new NodeVersionRef(); + version.setNode(getRef()); + return version; + } } public Map getNativeProperties() { @@ -2539,7 +2545,9 @@ public static List convertAlfrescoNodeRef(RepositoryDao repoDao, List convertEduNodeRef(RepositoryDao repoDao, List refs) { + return refs.stream().map(ref -> new NodeRef(repoDao, ref.getNodeId())).collect(Collectors.toList()); + } public void reportNode(String reason, String userEmail, String userComment) throws DAOException { try { String type = nodeService.getType(nodeId); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/OrganizationDao.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/OrganizationDao.java index 05bad36fd0..c66a4ae1b9 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/OrganizationDao.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/OrganizationDao.java @@ -9,6 +9,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.apache.commons.codec.digest.DigestUtils; import org.edu_sharing.alfresco.authentication.HttpContext; +import org.edu_sharing.alfresco.service.OrganisationService; import org.edu_sharing.repository.client.rpc.EduGroup; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.server.MCAlfrescoAPIClient; @@ -23,6 +24,7 @@ import org.edu_sharing.service.organization.OrganizationServiceFactory; import org.edu_sharing.service.organization.GroupSignupMethod; import org.edu_sharing.service.search.SearchServiceFactory; +import org.edu_sharing.spring.ApplicationContextFactory; public class OrganizationDao { @@ -196,9 +198,10 @@ public void delete() throws DAOException { throw new AccessDeniedException(currentUser); } - - ((MCAlfrescoAPIClient)repoDao.getBaseClient()).unbindEduGroupFolder(groupName, eduGroup.getFolderId()); - + + OrganisationService organisationService = (OrganisationService) ApplicationContextFactory.getApplicationContext().getBean("organisationService"); + organisationService.unbindEduGroupFolder(groupName,eduGroup.getFolderId()); + } catch (Exception e) { throw DAOException.mapping(e); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/PersonDao.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/PersonDao.java index d1e3516631..46a5a596b7 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/PersonDao.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/PersonDao.java @@ -97,7 +97,7 @@ private boolean isCurrentUserOrAdmin() { } return true; } - public static PersonDao createPerson(RepositoryDao repoDao, String userName,String password, UserProfileEdit profile) throws DAOException { + public static PersonDao createPerson(RepositoryDao repoDao, String userName,String password, UserProfileEdit profile, boolean returnResult) throws DAOException { try { @@ -114,10 +114,13 @@ public static PersonDao createPerson(RepositoryDao repoDao, String userName,Stri userInfo.put(CCConstants.PROP_USERNAME, userName); AuthorityServiceFactory.getAuthorityService(repoDao.getId()).createOrUpdateUser(userInfo); - PersonDao result=new PersonDao(repoDao, userName); - if(password!=null) - result.changePassword(null,password); - return result; + if(returnResult){ + PersonDao result=new PersonDao(repoDao, userName); + if(password!=null) + result.changePassword(null,password); + return result; + }else return null; + } } catch (Exception e) { diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/admin/v1/AdminApi.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/admin/v1/AdminApi.java index 80e5e69eab..632a2ecdb7 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/admin/v1/AdminApi.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/admin/v1/AdminApi.java @@ -102,13 +102,14 @@ public Response refreshAppInfo(@Context HttpServletRequest req) { } } + @GET @Path("/version") @Operation(summary = "get detailed version information", description="detailed information about the running system version") @ApiResponses(value = { - @ApiResponse(responseCode="200", description=RestConstants.HTTP_200, content = @Content(schema = @Schema(implementation = RepositoryVersionInfo.class))), + @ApiResponse(responseCode="200", description=RestConstants.HTTP_200, content = @Content(schema = @Schema(implementation = Map.class))), @ApiResponse(responseCode="400", description=RestConstants.HTTP_400, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), @ApiResponse(responseCode="401", description=RestConstants.HTTP_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), @ApiResponse(responseCode="403", description=RestConstants.HTTP_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), @@ -117,7 +118,7 @@ public Response refreshAppInfo(@Context HttpServletRequest req) { }) public Response getVersion(@Context HttpServletRequest req){ try { - RepositoryVersionInfo result = AdminServiceFactory.getInstance().getVersion(); + Map result = AdminServiceFactory.getInstance().getVersions(); return Response.ok().entity(result).build(); } catch (Throwable t) { return ErrorResponse.createResponse(t); @@ -553,38 +554,6 @@ public Response options3() { return Response.status(Response.Status.OK).header("Allow", "OPTIONS, POST").build(); } - @POST - @Path("/cache/refreshEduGroupCache") - - @Operation(summary = "Refresh the Edu Group Cache", description = "Refresh the Edu Group Cache.") - - @ApiResponses(value = { - @ApiResponse(responseCode="200", description=RestConstants.HTTP_200, content = @Content(schema = @Schema(implementation = Void.class))), - @ApiResponse(responseCode="400", description=RestConstants.HTTP_400, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode="401", description=RestConstants.HTTP_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode="403", description=RestConstants.HTTP_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode="404", description=RestConstants.HTTP_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), - @ApiResponse(responseCode="500", description=RestConstants.HTTP_500, content = @Content(schema = @Schema(implementation = ErrorResponse.class))) - }) - public Response refreshEduGroupCache( - @Parameter(description = "keep existing", schema = @Schema(defaultValue="false")) @QueryParam("keepExisting") Boolean keepExisting, - @Context HttpServletRequest req){ - try { - AdminServiceFactory.getInstance().refreshEduGroupCache(keepExisting); - return Response.ok().build(); - } catch (Throwable t) { - return ErrorResponse.createResponse(t); - } - } - - @OPTIONS - @Path("/cache/refreshEduGroupCache") - @Hidden - - public Response options4() { - - return Response.status(Response.Status.OK).header("Allow", "OPTIONS, GET").build(); - } @POST @Path("/cache/removeCacheEntry") diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/iam/v1/IamApi.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/iam/v1/IamApi.java index f28c88c92e..629b282d58 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/iam/v1/IamApi.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/iam/v1/IamApi.java @@ -13,6 +13,7 @@ import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.apache.log4j.Logger; +import org.edu_sharing.alfresco.policy.OnUpdatePersonPropertiesPolicy; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.server.MCAlfrescoAPIClient; import org.edu_sharing.repository.server.tools.ApplicationInfoList; @@ -434,12 +435,24 @@ public Response createUser( @Parameter(description = "username",required=true) @PathParam("person") String person, @Parameter(description = "profile" ,required=true ) UserProfileEdit profile, @Parameter(description = "Password, leave empty if you don't want to set any" ,required=false )@QueryParam("password") String password, + @Parameter(description = "returnResult, if true the created person object will be returned.", required=false ,schema = @Schema(defaultValue="true") ) @QueryParam("returnResult") boolean returnResult, + @Parameter(description = "setupHomeDir, if true the created persons homedir will be setup with the default folders.", required=false ,schema = @Schema(defaultValue="true") ) @QueryParam("setupHomeDir") boolean setupHomeDir, @Context HttpServletRequest req) { try { RepositoryDao repoDao = RepositoryDao.getRepository(repository); - User result = PersonDao.createPerson(repoDao, person,password, profile).asPerson(); + if(!setupHomeDir){ + OnUpdatePersonPropertiesPolicy.constructPersonFolders.set(false); + } + PersonDao personDao = PersonDao.createPerson(repoDao, person, password, profile,returnResult); + User result; + if(personDao == null){ + result = new User(); + result.setUserName(person); + }else{ + result = personDao.asPerson(); + } return Response.status(Response.Status.OK).entity(result).build(); @@ -462,7 +475,9 @@ public Response createUser( logger.error(t.getMessage(), t); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new ErrorResponse(t)).build(); - } + }finally { + OnUpdatePersonPropertiesPolicy.constructPersonFolders.set(null); + } } @PUT diff --git a/Backend/services/core/src/main/java/org/edu_sharing/restservices/shared/Pagination.java b/Backend/services/core/src/main/java/org/edu_sharing/restservices/shared/Pagination.java index 653b09e0e0..1e791a30a7 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/restservices/shared/Pagination.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/restservices/shared/Pagination.java @@ -7,6 +7,7 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonProperty; +import org.edu_sharing.repository.server.SearchResultNodeRef; @Schema(description = "") @@ -22,6 +23,11 @@ public Pagination(org.edu_sharing.service.search.model.SearchResult result) { total=result.getTotalCount(); count=result.getCount(); } + public Pagination(SearchResultNodeRef result) { + from=result.getStartIDX(); + total=result.getNodeCount(); + count=result.getData().size(); + } public Pagination(List result) { from=0; total=result.size(); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminService.java b/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminService.java index 2349f98a60..18d6f95df1 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminService.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminService.java @@ -112,8 +112,6 @@ void writePublisherToMDSXml(String vcardProps, String valueSpaceProp, String ign void setToolpermissions(String authority, Map toolpermissions) throws Throwable; - void refreshEduGroupCache(boolean keepExisting); - void testMail(String receiver, String template); String importOaiXml(InputStream xml, String recordHandlerClassName, String binaryHandlerClassName) throws Exception; @@ -129,5 +127,5 @@ void setToolpermissions(String authority, Collection getPlugins(); - RepositoryVersionInfo getVersion(); + Map getVersions(); } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminServiceImpl.java b/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminServiceImpl.java index 2fa2ea657a..836f0602a3 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminServiceImpl.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/admin/AdminServiceImpl.java @@ -1,17 +1,6 @@ package org.edu_sharing.service.admin; -import java.io.*; -import java.lang.management.ManagementFactory; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.text.Collator; -import java.util.*; -import java.util.stream.Collectors; - -import javax.management.MBeanServer; -import javax.management.ObjectName; +import com.google.common.io.Files; import jakarta.servlet.http.HttpSession; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -32,12 +21,13 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.ThreadContext; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; import org.apache.logging.log4j.core.config.LoggerConfig; -import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; import org.edu_sharing.alfresco.lightbend.LightbendConfigLoader; +import org.edu_sharing.alfresco.repository.server.authentication.Context; +import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; import org.edu_sharing.repository.client.exception.CCException; import org.edu_sharing.repository.client.rpc.ACE; import org.edu_sharing.repository.client.rpc.ACL; @@ -45,26 +35,27 @@ import org.edu_sharing.repository.client.rpc.cache.CacheInfo; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.repository.client.tools.StringTool; -import org.edu_sharing.repository.server.*; -import org.edu_sharing.alfresco.repository.server.authentication.Context; +import org.edu_sharing.repository.server.AuthenticationToolAPI; +import org.edu_sharing.repository.server.MCAlfrescoAPIClient; +import org.edu_sharing.repository.server.MCAlfrescoBaseClient; +import org.edu_sharing.repository.server.RepoFactory; import org.edu_sharing.repository.server.importer.ExcelLOMImporter; import org.edu_sharing.repository.server.importer.collections.CollectionImporter; import org.edu_sharing.repository.server.jobs.quartz.*; import org.edu_sharing.repository.server.jobs.quartz.annotation.JobFieldDescription; import org.edu_sharing.repository.server.tools.*; import org.edu_sharing.repository.server.tools.cache.CacheManagerFactory; -import org.edu_sharing.repository.server.tools.cache.EduGroupCache; import org.edu_sharing.repository.server.tools.mailtemplates.MailTemplate; import org.edu_sharing.repository.server.update.PrintWriterLogAppender; import org.edu_sharing.repository.server.update.UpdaterService; import org.edu_sharing.repository.tomcat.ClassHelper; -import org.edu_sharing.repository.update.*; +import org.edu_sharing.repository.tools.URLHelper; +import org.edu_sharing.repository.update.Protocol; import org.edu_sharing.restservices.GroupDao; import org.edu_sharing.restservices.RepositoryDao; import org.edu_sharing.restservices.admin.v1.model.PluginStatus; import org.edu_sharing.restservices.shared.Group; import org.edu_sharing.service.admin.model.GlobalGroup; -import org.edu_sharing.repository.server.jobs.quartz.JobInfo; import org.edu_sharing.service.admin.model.RepositoryConfig; import org.edu_sharing.service.admin.model.ServerUpdateInfo; import org.edu_sharing.service.admin.model.ToolPermission; @@ -87,9 +78,22 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import com.google.common.io.Files; - -import org.edu_sharing.repository.tools.URLHelper; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.text.Collator; +import java.util.*; +import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -671,15 +675,6 @@ public String runUpdate(String updateId, boolean execute) throws Exception { } } - @Override - public void refreshEduGroupCache(boolean keepExisting) { - if (keepExisting) { - EduGroupCache.refreshByKeepExisting(); - } else { - EduGroupCache.refresh(); - } - } - @Override public void testMail(String receiver, String template) { try { @@ -1141,7 +1136,7 @@ public Collection getPlugins() { } @Override - public RepositoryVersionInfo getVersion() { + public Map getVersions() { try { return versionService.getRepositoryVersionInfo(); } catch (IOException e) { diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthMethodSSO.java b/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthMethodSSO.java index a8f10b7e12..0a6da891ed 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthMethodSSO.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthMethodSSO.java @@ -73,6 +73,9 @@ public String authenticate(Map params) throws AuthenticationExce && springProfiles != null && (springProfiles.contains("openidEnabled") || springProfiles.contains("samlEnabled"))) { return ssoAuthorityMapper.mapAuthority(params); + }else if(SSOAuthorityMapper.SSO_TYPE_Shibboleth.equals(paramAuthType) + && config.getBoolean("security.sso.external.enabled")){ + return ssoAuthorityMapper.mapAuthority(params); }else if(SSOAuthorityMapper.SSO_TYPE_LTI.equals(paramAuthType) && config.getBoolean("security.sso.lti.enabled")){ return ssoAuthorityMapper.mapAuthority(params); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthenticationExceptionMessages.java b/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthenticationExceptionMessages.java index a504023413..f6d595eab0 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthenticationExceptionMessages.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/AuthenticationExceptionMessages.java @@ -44,6 +44,8 @@ public interface AuthenticationExceptionMessages { final public static String INVALID_AUTHENTICATION_METHOD = "INVALID_AUTHENTICATION_METHOD"; final public static String NOT_IN_WHITELIST = "NOT_IN_WHITELIST"; final public static String USER_BLOCKED = "USER_BLOCKED"; + final public static String SSO_REQ_ATT_PATTERN_SYNTAX = "SSO_REQ_ATT_PATTERN_SYNTAX"; + final public static String SSO_REQ_ATT_NOT_MATCHES = "SSO_REQ_ATT_NOT_MATCHES"; } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/SSOAuthorityMapper.java b/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/SSOAuthorityMapper.java index 5f761e0848..9420e99663 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/SSOAuthorityMapper.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/authentication/SSOAuthorityMapper.java @@ -33,6 +33,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; +import java.util.regex.PatternSyntaxException; /** * @@ -121,6 +122,8 @@ public class SSOAuthorityMapper { List additionalAttributes = new ArrayList<>(); + Map requiredAttributes; + public void init(){ ApplicationContext applicationContext = AlfAppContextGate.getApplicationContext(); @@ -132,6 +135,8 @@ public void init(){ //this.authenticationDao = (MutableAuthenticationDao)applicationContext.getBean("authenticationDao"); this.organisationService = (OrganisationService)applicationContext.getBean("eduOrganisationService"); this.nodeService = serviceRegistry.getNodeService(); + ApplicationContext eduApplicationContext = org.edu_sharing.spring.ApplicationContextFactory.getApplicationContext(); + requiredAttributes = eduApplicationContext.containsBean ("personDataRequired") ? (Map)eduApplicationContext.getBean("personDataRequired") : null; } public static String mapAdminAuthority(String authority,String appid){ @@ -210,6 +215,25 @@ private String mapAuthorityInternal(final Map ssoAttributes) { throw new AuthenticationException(AuthenticationExceptionMessages.MISSING_PARAM); } + + if(requiredAttributes != null && requiredAttributes.size() > 0){ + + requiredAttributes.keySet().stream().forEach(r -> { + String value = ssoAttributes.get(r); + if(value == null) + throw new AuthenticationException(AuthenticationExceptionMessages.MISSING_PARAM); + try { + if (!value.matches(requiredAttributes.get(r))) { + logger.debug("required attribute " + r + " " + value +" not matches " +requiredAttributes.get(r) ); + throw new AuthenticationException(AuthenticationExceptionMessages.SSO_REQ_ATT_NOT_MATCHES); + } + }catch (PatternSyntaxException e){ + logger.error("wrong required attribute pattern for:" + r + " pattern:"+requiredAttributes.get(r)+". " + e.getMessage()); + throw new AuthenticationException(AuthenticationExceptionMessages.SSO_REQ_ATT_PATTERN_SYNTAX); + } + }); + } + String appId = ssoAttributes.get(PARAM_APP_ID); ApplicationInfo appInfo = (appId != null) ? ApplicationInfoList.getRepositoryInfoById(appId) : null; boolean whitelistedUser = false; diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionService.java b/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionService.java index 6f9081c4ba..bc8dbab350 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionService.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionService.java @@ -9,6 +9,7 @@ import org.apache.commons.lang3.NotImplementedException; import org.edu_sharing.repository.client.rpc.ACE; import org.edu_sharing.repository.client.tools.CCConstants; +import org.edu_sharing.repository.server.SearchResultNodeRef; import org.edu_sharing.service.InsufficientPermissionException; import org.edu_sharing.service.search.model.SortDefinition; @@ -61,9 +62,12 @@ void proposeForCollection(String collectionId, String originalNodeId, String sou public void move(String toCollection, String nodeId); - public List getChildren(String parentId, String scope); - - public List getChildren(String parentId, String scope, SortDefinition sortDefinition,List filter); + + public List getChildren(String parentId, String scope, SortDefinition sortDefinition, List filter); + + List getRecentForCurrentUser() throws Throwable; + + public SearchResultNodeRef getRoot(String scope, SortDefinition sortDefinition, int skipCount, int maxItems) throws Throwable; public Collection get(org.edu_sharing.service.model.NodeRef collection, boolean fetchCounts, boolean resolveUsernames, BoolQuery readPermissionsQuery); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceElastic.java b/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceElastic.java index be340785b5..35dd061826 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceElastic.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceElastic.java @@ -9,16 +9,19 @@ import co.elastic.clients.json.JsonData; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.edu_sharing.metadataset.v2.MetadataSet; +import org.edu_sharing.metadataset.v2.tools.MetadataHelper; import org.edu_sharing.repository.client.tools.CCConstants; +import org.edu_sharing.repository.server.SearchResultNodeRef; import org.edu_sharing.service.authority.AuthorityServiceHelper; +import org.edu_sharing.service.search.SearchService; import org.edu_sharing.service.search.SearchServiceElastic; +import org.edu_sharing.service.search.SearchServiceFactory; +import org.edu_sharing.service.search.model.SearchToken; import org.edu_sharing.service.search.model.SortDefinition; import org.edu_sharing.spring.ApplicationContextFactory; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; public class CollectionServiceElastic extends CollectionServiceImpl { @@ -119,5 +122,32 @@ public List getChildrenProposal(String parentId) throws Exceptio public void proposeForCollection(String collectionId, String originalNodeId, String sourceRepositoryId) throws DuplicateNodeException, Throwable { super.proposeForCollection(collectionId, originalNodeId, sourceRepositoryId); } + + @Override + protected SearchResultNodeRef searchChildren(String scope, SortDefinition sortDefinition, int skipCount, int maxItems) throws Throwable { + + MetadataSet mds = MetadataHelper.getMetadataset(appInfo, CCConstants.metadatasetdefault_id); + + String queryId = getQueryForScope(scope); + /** + * @TODO owner + inherit off -> node will be found even if search is done in edu-group context + */ + + List returnVal = new ArrayList<>(); + SearchToken token = new SearchToken(); + token.setContentType(SearchService.ContentType.COLLECTIONS); + token.setSortDefinition(sortDefinition); + token.setFrom(skipCount); + token.setMaxResult(maxItems); + SearchResultNodeRef nodeRefs = SearchServiceFactory.getLocalService().search(mds, queryId, Collections.emptyMap(), token); + for (org.edu_sharing.service.model.NodeRef nodeRef : nodeRefs.getData()) { + if (isSubCollection(nodeRef)) { + continue; + } + returnVal.add(nodeRef); + } + nodeRefs.setData(returnVal); + return nodeRefs; + } } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceImpl.java b/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceImpl.java index 2cf9efb7f4..986043a950 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceImpl.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/collection/CollectionServiceImpl.java @@ -1,11 +1,5 @@ package org.edu_sharing.service.collection; -import java.io.InputStream; -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.BehaviourFilter; @@ -21,13 +15,13 @@ import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.lang3.NotImplementedException; import org.apache.log4j.Logger; import org.apache.lucene.queryparser.classic.QueryParser; +import org.edu_sharing.alfresco.repository.server.authentication.Context; import org.edu_sharing.alfresco.service.search.CMISSearchHelper; +import org.edu_sharing.alfresco.service.toolpermission.ToolPermissionException; import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate; -import org.edu_sharing.metadataset.v2.MetadataReader; -import org.edu_sharing.metadataset.v2.MetadataSet; -import org.edu_sharing.metadataset.v2.tools.MetadataHelper; import org.edu_sharing.repository.client.rpc.ACE; import org.edu_sharing.repository.client.rpc.ACL; import org.edu_sharing.repository.client.rpc.EduGroup; @@ -36,8 +30,11 @@ import org.edu_sharing.repository.server.AuthenticationTool; import org.edu_sharing.repository.server.MCAlfrescoAPIClient; import org.edu_sharing.repository.server.RepoFactory; -import org.edu_sharing.alfresco.repository.server.authentication.Context; -import org.edu_sharing.repository.server.tools.*; +import org.edu_sharing.repository.server.SearchResultNodeRef; +import org.edu_sharing.repository.server.tools.ApplicationInfo; +import org.edu_sharing.repository.server.tools.ApplicationInfoList; +import org.edu_sharing.repository.server.tools.I18nServer; +import org.edu_sharing.repository.server.tools.ImageTool; import org.edu_sharing.repository.server.tools.cache.PreviewCache; import org.edu_sharing.repository.server.tools.cache.RepositoryCache; import org.edu_sharing.repository.server.tools.forms.DuplicateFinder; @@ -48,6 +45,7 @@ import org.edu_sharing.service.InsufficientPermissionException; import org.edu_sharing.service.authority.AuthorityService; import org.edu_sharing.service.authority.AuthorityServiceFactory; +import org.edu_sharing.service.model.NodeRefImpl; import org.edu_sharing.service.nodeservice.NodeService; import org.edu_sharing.service.nodeservice.NodeServiceFactory; import org.edu_sharing.service.nodeservice.NodeServiceHelper; @@ -63,15 +61,21 @@ import org.edu_sharing.service.search.SearchServiceFactory; import org.edu_sharing.service.search.model.SearchToken; import org.edu_sharing.service.search.model.SortDefinition; -import org.edu_sharing.alfresco.service.toolpermission.ToolPermissionException; import org.edu_sharing.service.toolpermission.ToolPermissionHelper; import org.edu_sharing.service.toolpermission.ToolPermissionService; import org.edu_sharing.service.toolpermission.ToolPermissionServiceFactory; import org.edu_sharing.service.usage.Usage; import org.edu_sharing.service.usage.Usage2Service; import org.edu_sharing.spring.ApplicationContextFactory; +import org.jetbrains.annotations.Nullable; import org.springframework.context.ApplicationContext; +import java.io.InputStream; +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + public class CollectionServiceImpl implements CollectionService { @@ -283,9 +287,9 @@ private String addToCollection(String collectionId, String refNodeId, boolean al client.setProperty(refId, CCConstants.LOM_PROP_TECHNICAL_SIZE, (String) props.get(CCConstants.LOM_PROP_TECHNICAL_SIZE)); // run setUsage as admin because the user may not has cc_publish on the original node (but on the ref) - new Usage2Service().setUsageInternal(appInfo.getAppId(), + new Usage2Service().setUsageInternal(ApplicationInfoList.getHomeRepository().getAppId(), authInfo.get(CCConstants.AUTH_USERNAME), - appInfo.getAppId(), + ApplicationInfoList.getHomeRepository().getAppId(), collectionId, originalNodeId, null, null, null, -1, versLabel, refId, null); @@ -356,7 +360,7 @@ public void proposeForCollection(String collectionId, String originalNodeId, Str AuthenticationUtil.runAsSystem(() -> { NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, finalId); if (getChildren(collectionId, null, new SortDefinition(), Collections.singletonList("files")).stream().anyMatch((ref) -> - nodeRef.getId().equals(NodeServiceHelper.getProperty(ref, CCConstants.CCM_PROP_IO_ORIGINAL)) + nodeRef.getId().equals(NodeServiceHelper.getProperty(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeRef.getId()), CCConstants.CCM_PROP_IO_ORIGINAL)) )) { throw new DuplicateNodeException("Node id " + nodeRef.getId() + " is already in this collection"); } @@ -773,95 +777,29 @@ public Void doWork() throws Exception { } }); } - @Override - public List getChildren(String parentId, String scope) { - - try { - List returnVal = new ArrayList<>(); - if (parentId == null) { - - /** - * @TODO owner + inherit off -> node will be found even if search is done in edu-group context - * level 0 nodes -> maybe cache level 0 with an node property - */ - String queryString = "ASPECT:\"" + CCConstants.CCM_ASPECT_COLLECTION + "\"" + " AND @ccm\\:collectionlevel0:true"; - MCAlfrescoAPIClient.ContextSearchMode mode = getContextModeForScope(scope); - if (SearchScope.MY.name().equals(scope)) { - queryString += " AND OWNER:\"" + authInfo.get(CCConstants.AUTH_USERNAME) + "\""; - } - SearchParameters token = new SearchParameters(); - token.setQuery(queryString); - List searchResult = client.searchNodeRefs(token, mode); - for (NodeRef entry : searchResult) { - String parent = nodeService.getPrimaryParent(entry.getStoreRef().getProtocol(), entry.getStoreRef().getIdentifier(), entry.getId()); - if (Arrays.asList(client.getAspects(parent)).contains(CCConstants.CCM_ASPECT_COLLECTION)) { - continue; - } - returnVal.add(entry); - } - } else { - List list = nodeService.getChildrenChildAssociationRef(parentId); - for (ChildAssociationRef entry : list) { - returnVal.add(entry.getChildRef()); - } - } - return returnVal; - } catch (Throwable e) { - throw new RuntimeException(e.getMessage()); - } + public List getRecentForCurrentUser() throws Throwable { + return permissionService.getRecentProperty(CCConstants.CCM_PROP_PERSON_RECENT_COLLECTIONS).stream().map( + ref -> new NodeRefImpl(ref.getId()) + ).collect(Collectors.toList()); + } + @Override + public SearchResultNodeRef getRoot(String scope, SortDefinition sortDefinition, int skipCount, int maxItems) throws Throwable { + return searchChildren(scope, sortDefinition, skipCount, maxItems); } @Override - public List getChildren(String parentId, String scope, SortDefinition sortDefinition, List filter) { + public List getChildren(String parentId, String scope, SortDefinition sortDefinition, List filter) { try { if (parentId == null) { - SearchParameters searchParams = new SearchParameters(); - sortDefinition.applyToSearchParameters(searchParams); - - MetadataSet mds = MetadataHelper.getMetadataset(appInfo, CCConstants.metadatasetdefault_id); - - String queryId = null; - switch (SearchScope.valueOf(scope)) { - case MY: - queryId = "collections_scope_my"; - break; - case EDU_ALL: - queryId = "collections_scope_public"; - break; - case EDU_GROUPS: - queryId = "collections_scope_shared"; - break; - case TYPE_EDITORIAL: - queryId = "collections_scope_editorial"; - break; - case TYPE_MEDIA_CENTER: - queryId = "collections_scope_media_center"; - break; - case RECENT: - return permissionService.getRecentProperty(CCConstants.CCM_PROP_PERSON_RECENT_COLLECTIONS); - } - String queryString = mds.findQuery(queryId, MetadataReader.QUERY_SYNTAX_LUCENE).findBasequery(null); - /** - * @TODO owner + inherit off -> node will be found even if search is done in edu-group context - */ - MCAlfrescoAPIClient.ContextSearchMode mode = getContextModeForScope(scope); - - List returnVal = new ArrayList<>(); - searchParams.setQuery(queryString); - List nodeRefs = client.searchNodeRefs(searchParams, mode); - for (NodeRef nodeRef : nodeRefs) { - if (isSubCollection(nodeRef)) { - continue; - } - returnVal.add(nodeRef); - } - return returnVal; + throw new IllegalArgumentException("parentId must be set, please use getRoot() instead"); } else { List children = nodeService.getChildrenChildAssociationRefAssoc(parentId, null, filter, sortDefinition); - List returnVal = new ArrayList<>(); + List returnVal = new ArrayList<>(); for (ChildAssociationRef child : children) { - returnVal.add(child.getChildRef()); + returnVal.add(new NodeRefImpl( + child.getChildRef().getId() + )); } return returnVal; } @@ -870,7 +808,34 @@ public List getChildren(String parentId, String scope, SortDefinition s } } - private MCAlfrescoAPIClient.ContextSearchMode getContextModeForScope(String scope) { + protected SearchResultNodeRef searchChildren(String scope, SortDefinition sortDefinition, int skipCount, int maxItems) throws Throwable { + throw new NotImplementedException("Searching collections is not supported without elastic"); + } + + @Nullable + protected static String getQueryForScope(String scope) { + String queryId = null; + switch (SearchScope.valueOf(scope)) { + case MY: + queryId = "collections_scope_my"; + break; + case EDU_ALL: + queryId = "collections_scope_public"; + break; + case EDU_GROUPS: + queryId = "collections_scope_shared"; + break; + case TYPE_EDITORIAL: + queryId = "collections_scope_editorial"; + break; + case TYPE_MEDIA_CENTER: + queryId = "collections_scope_media_center"; + break; + } + return queryId; + } + + protected MCAlfrescoAPIClient.ContextSearchMode getContextModeForScope(String scope) { MCAlfrescoAPIClient.ContextSearchMode mode = MCAlfrescoAPIClient.ContextSearchMode.Default; if (Scope.EDU_GROUPS.name().equals(scope)) { mode = MCAlfrescoAPIClient.ContextSearchMode.UserAndGroups; @@ -880,10 +845,10 @@ private MCAlfrescoAPIClient.ContextSearchMode getContextModeForScope(String scop return mode; } - private boolean isSubCollection(NodeRef nodeRef) { + protected boolean isSubCollection(org.edu_sharing.service.model.NodeRef nodeRef) { return AuthenticationUtil.runAsSystem(() -> { - String parent = client.getParent(nodeRef).getParentRef().getId(); - return Arrays.asList(client.getAspects(parent)).contains(CCConstants.CCM_ASPECT_COLLECTION); + NodeRef parent = NodeServiceHelper.getPrimaryParent(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeRef.getNodeId())); + return NodeServiceHelper.hasAspect(parent, CCConstants.CCM_ASPECT_COLLECTION); }); } @@ -1039,7 +1004,7 @@ public void removePreviewImage(String collectionId) throws Exception { @Override public void setOrder(String parentId, String[] nodes) { - List refs=getChildren(parentId, null, new SortDefinition(),Arrays.asList("files", "folders")); + List refs=getChildren(parentId, null, new SortDefinition(),Arrays.asList("files", "folders")); AtomicInteger order=new AtomicInteger(0); Map collectionProps = new HashMap<>(); @@ -1051,7 +1016,7 @@ public void setOrder(String parentId, String[] nodes) { transactionService.getRetryingTransactionHelper().doInTransaction(() -> { NodeRef ref = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, node); policyBehaviourFilter.disableBehaviour(ref, ContentModel.ASPECT_AUDITABLE); - if (!refs.contains(ref)) + if (!refs.contains(new NodeRefImpl(ref.getId()))) throw new IllegalArgumentException("Node id " + node + " is not a children of the collection " + parentId); nodeService.addAspect(node, CCConstants.CCM_ASPECT_COLLECTION_ORDERED); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/model/NodeRefImpl.java b/Backend/services/core/src/main/java/org/edu_sharing/service/model/NodeRefImpl.java index fda7494054..eea6717fe2 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/model/NodeRefImpl.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/model/NodeRefImpl.java @@ -1,9 +1,6 @@ package org.edu_sharing.service.model; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.edu_sharing.repository.client.tools.CCConstants; import org.edu_sharing.restservices.shared.Contributor; @@ -220,4 +217,17 @@ public void setContributors(List contributors) { public List getContributors() { return contributors; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NodeRefImpl nodeRef = (NodeRefImpl) o; + return Objects.equals(storeProtocol, nodeRef.storeProtocol) && Objects.equals(storeId, nodeRef.storeId) && Objects.equals(nodeId, nodeRef.nodeId); + } + + @Override + public int hashCode() { + return Objects.hash(storeProtocol, storeId, nodeId); + } } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/nodeservice/NodeFrontpage.java b/Backend/services/core/src/main/java/org/edu_sharing/service/nodeservice/NodeFrontpage.java index 3c1338efd3..e1275f3026 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/nodeservice/NodeFrontpage.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/nodeservice/NodeFrontpage.java @@ -67,17 +67,7 @@ public Collection getNodesForCurrentUserAndConfig() throws Throwable { sortDefinition.addSortDefinitionEntry( new SortDefinition.SortDefinitionEntry(CCConstants.getValidLocalName(CCConstants.CCM_PROP_COLLECTION_ORDERED_POSITION),true),0); - Collection alfNodeRef = CollectionServiceFactory.getLocalService().getChildren(config.collection, null,sortDefinition, Collections.singletonList("files")); - Collection result = new ArrayList<>(); - alfNodeRef.stream().forEach((n)->{ - NodeRef nodeRef = new NodeRefImpl(); - nodeRef.setNodeId(n.getId()); - nodeRef.setStoreId(n.getStoreRef().getIdentifier()); - nodeRef.setStoreProtocol(n.getStoreRef().getProtocol()); - result.add(nodeRef); - }); - - return result; + return CollectionServiceFactory.getLocalService().getChildren(config.collection, null,sortDefinition, Collections.singletonList("files")); } BoolQuery.Builder query = new BoolQuery.Builder() diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/provider/Provider.java b/Backend/services/core/src/main/java/org/edu_sharing/service/provider/Provider.java index a3b2003711..5f14da5dec 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/provider/Provider.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/provider/Provider.java @@ -55,7 +55,7 @@ public AuthenticationTool getAuthenticationTool(){ } public RenderingService getRenderingService(){ RenderingService service = (RenderingService) ApplicationContextFactory.getApplicationContext().getBean("renderingService"); - service.setAppId(ApplicationInfoList.getHomeRepository().getAppId()); + service.setAppId(appId); return service; } public CollectionService getCollectionService(){ diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/rendering/RenderingServiceImpl.java b/Backend/services/core/src/main/java/org/edu_sharing/service/rendering/RenderingServiceImpl.java index 32c67e2bbb..71dd7b0e0e 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/rendering/RenderingServiceImpl.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/rendering/RenderingServiceImpl.java @@ -175,7 +175,9 @@ public RenderingServiceData getData(ApplicationInfo appInfo, String nodeId, Stri long time=System.currentTimeMillis(); NodeService nodeService = NodeServiceFactory.getNodeService(appInfo.getAppId()); RenderingServiceData data=new RenderingServiceData(); - data.setEditors(getAvailableEditors(nodeId, nodeVersion, user)); + if(appInfo.ishomeNode()) { + data.setEditors(getAvailableEditors(nodeId, nodeVersion, user)); + } RepositoryDao repoDao = RepositoryDao.getRepository(this.appInfo.getAppId()); NodeDao nodeDao = NodeDao.getNodeWithVersion(repoDao, nodeId, nodeVersion); diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/search/SearchServiceElastic.java b/Backend/services/core/src/main/java/org/edu_sharing/service/search/SearchServiceElastic.java index cdaa68b696..7ca31f03e0 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/search/SearchServiceElastic.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/search/SearchServiceElastic.java @@ -114,7 +114,7 @@ public static HttpHost[] getConfiguredHosts() { public static BoolQuery getFilesSharedToMeQuery(MetadataQueries queries, SharedToMeType type) { String username = AuthenticationUtil.getFullyAuthenticatedUser(); Set memberships = getAllMemberships(username); - String basequery = queries.findQuery("sharedToMe").getBasequery().get(null); + String basequery = queries.findQuery("sharedToMe").getPrimaryBasequery(); BoolQuery.Builder builder = QueryBuilders.bool() .must(b -> b.bool(b2 -> b2.mustNot( @@ -150,7 +150,7 @@ private static Set getAllMemberships(String username) { public static BoolQuery getFilesSharedByMeQuery(MetadataQueries queries) { String username = AuthenticationUtil.getFullyAuthenticatedUser(); - String basequery = queries.findQuery("sharedByMe").getBasequery().get(null); + String basequery = queries.findQuery("sharedByMe").getPrimaryBasequery(); BoolQuery.Builder builder = QueryBuilders.bool() .must(b -> b.bool(b2 -> b2.must( b3 -> b3.match(m -> m.field("properties.ccm:ph_users.keyword").query(username)) @@ -1454,7 +1454,7 @@ public SearchResultNodeRef getWorkflowReceive(String user, SortDefinition sortDe builder.mustNot(b -> b.match(m -> m.field("aspects").query(CCConstants.getValidLocalName(CCConstants.CCM_ASPECT_COLLECTION_IO_REFERENCE)))); builder.must(getContentTypeQuery(contentType)); MetadataQueries queries = MetadataHelper.getLocalDefaultMetadataset().getQueries(MetadataReader.QUERY_SYNTAX_DSL); - String basequery = queries.findQuery("workflowReceive").getBasequery().get(null); + String basequery = queries.findQuery("workflowReceive").getPrimaryBasequery(); if(StringUtils.isNotBlank(basequery)) { builder.must(b -> b.wrapper(new ReadableWrapperQueryBuilder(basequery).build())); } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/version/RepositoryVersionInfo.java b/Backend/services/core/src/main/java/org/edu_sharing/service/version/RepositoryVersionInfo.java index 6fe279d668..a3cba48b21 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/version/RepositoryVersionInfo.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/version/RepositoryVersionInfo.java @@ -1,12 +1,11 @@ package org.edu_sharing.service.version; import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; public class RepositoryVersionInfo implements Serializable { + public String repository; public Version version; - public VersionMaven maven; +// public VersionMaven maven; public VersionGit git; public VersionBuild build; @@ -36,15 +35,15 @@ public static class VersionTimestamp implements Serializable { public String datetime; } - - public static class VersionMaven implements Serializable { - public Map bom; - public VersionProject project; - - public static class VersionProject implements Serializable { - public String artifactId; - public String groupId; - public String version; - } - } +// +// public static class VersionMaven implements Serializable { +// public HashMap bom; +// public VersionProject project; +// +// public static class VersionProject implements Serializable { +// public String artifactId; +// public String groupId; +// public String version; +// } +// } } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/service/version/VersionService.java b/Backend/services/core/src/main/java/org/edu_sharing/service/version/VersionService.java index fcc1e1999f..8129732344 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/service/version/VersionService.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/service/version/VersionService.java @@ -1,13 +1,5 @@ package org.edu_sharing.service.version; -import java.io.*; -import java.util.Arrays; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import com.google.gson.Gson; import org.alfresco.repo.cache.SimpleCache; import org.apache.commons.io.IOUtils; @@ -21,11 +13,24 @@ import org.edu_sharing.service.rendering.RenderingVersionInfo; import org.edu_sharing.spring.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.context.ApplicationListener; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Service; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + @Service public class VersionService implements ApplicationListener { - static Logger logger = Logger.getLogger(VersionService.class); + static Logger logger = Logger.getLogger(VersionService.class); public SimpleCache versionCache = (SimpleCache) AlfAppContextGate.getApplicationContext().getBean("eduSharingVersionCache", SimpleCache.class); @@ -37,133 +42,139 @@ public void onApplicationEvent(RefreshScopeRefreshedEvent event) { public Licenses getLicenses() { - try { - Licenses licenses = new Licenses(); - String path = System.getProperty("catalina.base") + "/webapps/"; - try { - for (File file : - Stream.concat( - Arrays.stream(new File(path + "alfresco/WEB-INF/licenses") - .listFiles((dir, name) -> name.toLowerCase().endsWith(".txt"))), - Arrays.stream(new File(path + "edu-sharing/WEB-INF/licenses") - .listFiles((dir, name) -> name.toLowerCase().endsWith(".txt") - )) - - ).collect(Collectors.toList())) { - try (FileInputStream fis = new FileInputStream(file)) { - licenses.getRepository().put(file.getName(), IOUtils.toString(fis)); - } - } - } catch(Throwable t) { - logger.error(t.getMessage(), t); - } - try { - licenses.getServices().put( - Services.Rendering, - RenderingServiceFactory.getLocalService().getVersion().licenses - ); - } catch(Throwable t) { - logger.error(t.getMessage(), t); - } - return cleanLicenses(licenses); - } catch(Exception e) { - throw new RuntimeException(e); - } - } + try { + Licenses licenses = new Licenses(); + String path = System.getProperty("catalina.base") + "/webapps/"; + try { + for (File file : + Stream.concat( + Arrays.stream(new File(path + "alfresco/WEB-INF/licenses") + .listFiles((dir, name) -> name.toLowerCase().endsWith(".txt"))), + Arrays.stream(new File(path + "edu-sharing/WEB-INF/licenses") + .listFiles((dir, name) -> name.toLowerCase().endsWith(".txt") + )) + + ).collect(Collectors.toList())) { + try (FileInputStream fis = new FileInputStream(file)) { + licenses.getRepository().put(file.getName(), IOUtils.toString(fis)); + } + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } + try { + licenses.getServices().put( + Services.Rendering, + RenderingServiceFactory.getLocalService().getVersion().licenses + ); + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } + return cleanLicenses(licenses); + } catch (Exception e) { + throw new RuntimeException(e); + } + } private Licenses cleanLicenses(Licenses licenses) { - boolean isAdmin = false; - try{ - isAdmin = AuthorityServiceHelper.isAdmin(); - } catch(Throwable ignored) { } - LicenseDisclosure disclosure = LightbendConfigLoader.get().getEnum(LicenseDisclosure.class, "repository.disclosure.licenses"); - if(isAdmin || disclosure.equals(LicenseDisclosure.all)) { - return licenses; - } - if(disclosure.equals(LicenseDisclosure.off)) { - throw new SecurityException("license disclosure is disabled"); - } - if(disclosure.equals(LicenseDisclosure.minimal)) { - cleanupLicenseList(licenses.getRepository()); - licenses.getServices().forEach((key, value) -> { - cleanupLicenseList(value); - }); - return licenses; - } - throw new SecurityException("license disclosure mode is unknown"); - } + boolean isAdmin = false; + try { + isAdmin = AuthorityServiceHelper.isAdmin(); + } catch (Throwable ignored) { + } + LicenseDisclosure disclosure = LightbendConfigLoader.get().getEnum(LicenseDisclosure.class, "repository.disclosure.licenses"); + if (isAdmin || disclosure.equals(LicenseDisclosure.all)) { + return licenses; + } + if (disclosure.equals(LicenseDisclosure.off)) { + throw new SecurityException("license disclosure is disabled"); + } + if (disclosure.equals(LicenseDisclosure.minimal)) { + cleanupLicenseList(licenses.getRepository()); + licenses.getServices().forEach((key, value) -> { + cleanupLicenseList(value); + }); + return licenses; + } + throw new SecurityException("license disclosure mode is unknown"); + } private void cleanupLicenseList(Map licenses) { - licenses.forEach((key, value) -> { - Pattern pattern = Pattern.compile("(\\(.*\\).*\\(.*)@([\\w|\\d|\\.]*)([\\s|\\)].*)"); - Matcher m = pattern.matcher(value); - value = m.replaceAll("$1$3"); - pattern = Pattern.compile("(\\(.*\\).*\\(.*)\\:([\\w|\\d|\\.-]*)([\\s|\\)].*)"); - m = pattern.matcher(value); - value = m.replaceAll("$1$3"); - licenses.put(key, value); - }); - } + licenses.forEach((key, value) -> { + Pattern pattern = Pattern.compile("(\\(.*\\).*\\(.*)@([\\w|\\d|\\.]*)([\\s|\\)].*)"); + Matcher m = pattern.matcher(value); + value = m.replaceAll("$1$3"); + pattern = Pattern.compile("(\\(.*\\).*\\(.*)\\:([\\w|\\d|\\.-]*)([\\s|\\)].*)"); + m = pattern.matcher(value); + value = m.replaceAll("$1$3"); + licenses.put(key, value); + }); + } - public static enum Type{ - REPOSITORY, - RENDERSERVICE - } + public enum Type { + REPOSITORY, + RENDERSERVICE + } private String VERSION_FILE="version.json"; public String getVersionNoException(Type type){ - try { - return getVersion(type); - }catch(Exception e) { - return "unknown"; - } - } + try { + return getVersion(type); + } catch (Exception e) { + return "unknown"; + } + } public String getVersion(Type type) throws Exception{ - if(versionCache.getKeys().contains(type)){ - return versionCache.get(type); - } - String value; - if(type.equals(Type.REPOSITORY)) { - value=getRepositoryVersion(); - }else if(type.equals(Type.RENDERSERVICE)) { - value=getRenderserviceVersion(); - // cleanup version and only keep major + minor - try { - Pattern pattern = Pattern.compile("(\\d\\.\\d)\\..*"); - Matcher m = pattern.matcher(value); - value = m.replaceAll("$1"); - }catch(Exception e) { - logger.warn("Error while escaping rs version:", e); - } - }else { - throw new IllegalArgumentException("Unknown type "+type); - } - versionCache.put(type,value); - return value; - } + if (versionCache.getKeys().contains(type)) { + return versionCache.get(type); + } + String value; + if (type.equals(Type.REPOSITORY)) { + value = getRepositoryVersion(); + } else if (type.equals(Type.RENDERSERVICE)) { + value = getRenderserviceVersion(); + // cleanup version and only keep major + minor + try { + Pattern pattern = Pattern.compile("(\\d\\.\\d)\\..*"); + Matcher m = pattern.matcher(value); + value = m.replaceAll("$1"); + } catch (Exception e) { + logger.warn("Error while escaping rs version:", e); + } + } else { + throw new IllegalArgumentException("Unknown type " + type); + } + versionCache.put(type, value); + return value; + } public void invalidateCache(){ - versionCache.clear(); - } + versionCache.clear(); + } private String getRenderserviceVersion() throws Exception{ - RenderingVersionInfo version = RenderingServiceFactory.getLocalService().getVersion(); - if(version != null) { - return version.version; - } - return null; - } + RenderingVersionInfo version = RenderingServiceFactory.getLocalService().getVersion(); + if (version != null) { + return version.version; + } + return null; + } private String getRepositoryVersion() throws Exception{ - RepositoryVersionInfo.Version version = getRepositoryVersionInfo().version; - return version.major + "." + version.minor; - } - public RepositoryVersionInfo getRepositoryVersionInfo() throws IOException { - return - new Gson().fromJson( - String.join( - "", - IOUtils.readLines( - VersionService.class.getClassLoader().getResourceAsStream("version.json") - ) - ), - RepositoryVersionInfo.class - ); - } + RepositoryVersionInfo.Version version = getRepositoryVersionInfo().get("project").version; + return version.major + "." + version.minor; + } + + public Map getRepositoryVersionInfo() throws IOException { + + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + List resources = new ArrayList<>(List.of(resolver.getResources("config/defaults/*-version.json"))); + resources.addAll(List.of(resolver.getResources("config/plugins/**/version.json"))); + + Map versions = new LinkedHashMap<>(); + for (Resource resource : resources) { + try(InputStream is = resource.getInputStream()) { + RepositoryVersionInfo versionInfo = new Gson().fromJson(IOUtils.toString(is, StandardCharsets.UTF_8), RepositoryVersionInfo.class); + versions.put(versionInfo.repository, versionInfo); + } + } + + return versions; + } } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/spring/security/basic/CommonSecurityConfiguration.java b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/basic/CommonSecurityConfiguration.java new file mode 100644 index 0000000000..689593b3a4 --- /dev/null +++ b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/basic/CommonSecurityConfiguration.java @@ -0,0 +1,21 @@ +package org.edu_sharing.spring.security.basic; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.firewall.StrictHttpFirewall; + + +@Profile({"basic","samlEnabled","openidEnabled"}) +@Configuration +public class CommonSecurityConfiguration { + @Bean + public StrictHttpFirewall httpFirewall() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + //config for allowing urls like: edu-sharing/rest/rendering/v1/details/brockhaus/%252fjulex%252farticle%252fschule?version=-1 + firewall.setAllowUrlEncodedPercent(true); + firewall.setAllowUrlEncodedSlash(true); + return firewall; + } +} diff --git a/Backend/services/core/src/main/java/org/edu_sharing/spring/security/basic/HeadersConfig.java b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/basic/HeadersConfig.java new file mode 100644 index 0000000000..076491e08d --- /dev/null +++ b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/basic/HeadersConfig.java @@ -0,0 +1,21 @@ +package org.edu_sharing.spring.security.basic; + +import org.edu_sharing.repository.server.SecurityHeadersFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.header.writers.StaticHeadersWriter; + +import java.util.Map; + +public class HeadersConfig { + public static void config(HttpSecurity http) throws Exception { + //disable frame options here so that header config is used + http.headers(h -> h.frameOptions(fo -> fo.disable())); + for (Map.Entry h : SecurityHeadersFilter.getConfiguredHeaders() + .entrySet()) { + http.headers(c -> c.addHeaderWriter(new StaticHeadersWriter(h.getKey(), h.getValue()))); + } + } + +} diff --git a/Backend/services/core/src/main/java/org/edu_sharing/spring/security/openid/SecurityConfigurationOpenIdConnect.java b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/openid/SecurityConfigurationOpenIdConnect.java index 1a73c77965..422549ed8e 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/spring/security/openid/SecurityConfigurationOpenIdConnect.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/openid/SecurityConfigurationOpenIdConnect.java @@ -12,6 +12,7 @@ import org.edu_sharing.spring.security.basic.CSRFConfig; import org.edu_sharing.spring.security.basic.EduAuthSuccsessHandler; import org.edu_sharing.spring.security.basic.EduWebSecurityCustomizer; +import org.edu_sharing.spring.security.basic.HeadersConfig; import org.edu_sharing.spring.security.openid.config.OpenIdConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -98,6 +99,7 @@ SecurityFilterChain app(HttpSecurity http) throws Exception { ).headers(h -> h.frameOptions(f -> f.disable())); CSRFConfig.config(http); + HeadersConfig.config(http); return http.build(); } diff --git a/Backend/services/core/src/main/java/org/edu_sharing/spring/security/saml2/SecurityConfigurationSaml.java b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/saml2/SecurityConfigurationSaml.java index 69075a82f6..d410b70c9c 100644 --- a/Backend/services/core/src/main/java/org/edu_sharing/spring/security/saml2/SecurityConfigurationSaml.java +++ b/Backend/services/core/src/main/java/org/edu_sharing/spring/security/saml2/SecurityConfigurationSaml.java @@ -7,16 +7,19 @@ import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.edu_sharing.alfresco.lightbend.LightbendConfigLoader; +import org.edu_sharing.repository.server.SecurityHeadersFilter; import org.edu_sharing.repository.server.tools.ApplicationInfoList; import org.edu_sharing.service.config.ConfigServiceFactory; import org.edu_sharing.spring.security.basic.CSRFConfig; import org.edu_sharing.spring.security.basic.EduAuthSuccsessHandler; import org.edu_sharing.spring.security.basic.EduWebSecurityCustomizer; +import org.edu_sharing.spring.security.basic.HeadersConfig; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; import org.springframework.core.convert.converter.Converter; import org.springframework.core.io.ClassPathResource; import org.springframework.security.config.annotation.ObjectPostProcessor; @@ -104,6 +107,7 @@ SecurityFilterChain app(HttpSecurity http) throws Exception { .saml2Metadata(withDefaults()); CSRFConfig.config(http); + HeadersConfig.config(http); return http.build(); } @@ -225,9 +229,13 @@ RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { */ String getRelyingPartyId(RelyingPartyRegistration.Builder b){ try { - URI uri = new URI(b.build().getAssertingPartyDetails().getEntityId()); + String entityId = b.build().getAssertingPartyDetails().getEntityId(); + if(!entityId.startsWith("http://") && !entityId.startsWith("https://")) { + entityId = "http://" + entityId; + } + URI uri = new URI(entityId); String host = uri.getHost(); - return host; + return host != null ? host : uri.toString(); } catch (URISyntaxException e) { return UUID.randomUUID().toString(); } diff --git a/Backend/services/core/src/main/resources/org/edu_sharing/spring/edu-sharing-sso-context.xml b/Backend/services/core/src/main/resources/org/edu_sharing/spring/edu-sharing-sso-context.xml index 2e7178b805..7694bdca5f 100644 --- a/Backend/services/core/src/main/resources/org/edu_sharing/spring/edu-sharing-sso-context.xml +++ b/Backend/services/core/src/main/resources/org/edu_sharing/spring/edu-sharing-sso-context.xml @@ -39,6 +39,15 @@ + + + diff --git a/Backend/services/rest/api/src/main/resources/openapi.json b/Backend/services/rest/api/src/main/resources/openapi.json index 973fd60679..6c890d76e8 100644 --- a/Backend/services/rest/api/src/main/resources/openapi.json +++ b/Backend/services/rest/api/src/main/resources/openapi.json @@ -4899,23 +4899,6 @@ }, "type": "object" }, - "RepositoryVersionInfo": { - "properties": { - "build": { - "$ref": "#/components/schemas/VersionBuild" - }, - "git": { - "$ref": "#/components/schemas/VersionGit" - }, - "maven": { - "$ref": "#/components/schemas/VersionMaven" - }, - "version": { - "$ref": "#/components/schemas/Version" - } - }, - "type": "object" - }, "RestoreResult": { "properties": { "archiveNodeId": { @@ -6759,95 +6742,6 @@ }, "type": "object" }, - "Version": { - "properties": { - "build": { - "type": "string" - }, - "full": { - "type": "string" - }, - "major": { - "type": "string" - }, - "minor": { - "type": "string" - }, - "patch": { - "type": "string" - }, - "qualifier": { - "type": "string" - } - }, - "type": "object" - }, - "VersionBuild": { - "properties": { - "timestamp": { - "type": "string" - } - }, - "type": "object" - }, - "VersionGit": { - "properties": { - "branch": { - "type": "string" - }, - "commit": { - "$ref": "#/components/schemas/VersionGitCommit" - } - }, - "type": "object" - }, - "VersionGitCommit": { - "properties": { - "id": { - "type": "string" - }, - "timestamp": { - "$ref": "#/components/schemas/VersionTimestamp" - } - }, - "type": "object" - }, - "VersionMaven": { - "properties": { - "bom": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "project": { - "$ref": "#/components/schemas/VersionProject" - } - }, - "type": "object" - }, - "VersionProject": { - "properties": { - "artifactId": { - "type": "string" - }, - "groupId": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "type": "object" - }, - "VersionTimestamp": { - "properties": { - "datetime": { - "type": "string" - } - }, - "type": "object" - }, "WebsiteInformation": { "properties": { "description": { @@ -8024,85 +7918,6 @@ ] } }, - "/admin/v1/cache/refreshEduGroupCache": { - "post": { - "description": "Refresh the Edu Group Cache.", - "operationId": "refreshEduGroupCache", - "parameters": [ - { - "description": "keep existing", - "in": "query", - "name": "keepExisting", - "schema": { - "default": false, - "type": "boolean" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": {} - }, - "description": "OK." - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Preconditions are not present." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Authorization failed." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Session user has insufficient rights to perform this operation." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Ressources are not found." - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Fatal error occured." - } - }, - "summary": "Refresh the Edu Group Cache", - "tags": [ - "ADMIN v1" - ] - } - }, "/admin/v1/cache/removeCacheEntry": { "post": { "description": "remove cache entry", @@ -11893,7 +11708,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RepositoryVersionInfo" + "type": "string" } } }, @@ -18105,6 +17920,24 @@ "schema": { "type": "string" } + }, + { + "description": "returnResult, if true the created person object will be returned.", + "in": "query", + "name": "returnResult", + "schema": { + "default": true, + "type": "boolean" + } + }, + { + "description": "setupHomeDir, if true the created persons homedir will be setup with the default folders.", + "in": "query", + "name": "setupHomeDir", + "schema": { + "default": true, + "type": "boolean" + } } ], "requestBody": { diff --git a/Backend/transform/src/main/java/org/edu_sharing/alfresco/transformer/extractors/ZipMetadataExtractor.java b/Backend/transform/src/main/java/org/edu_sharing/alfresco/transformer/extractors/ZipMetadataExtractor.java index 5240bf199f..65ec10acb9 100644 --- a/Backend/transform/src/main/java/org/edu_sharing/alfresco/transformer/extractors/ZipMetadataExtractor.java +++ b/Backend/transform/src/main/java/org/edu_sharing/alfresco/transformer/extractors/ZipMetadataExtractor.java @@ -4,6 +4,7 @@ import org.alfresco.transform.base.metadata.AbstractMetadataExtractorEmbedder; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.tika.utils.StringUtils; import org.edu_sharing.alfresco.transformer.executors.tools.ZipTool; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; @@ -58,7 +59,9 @@ public Map extractMetadata(String sourceMimetype, InputStr Reader reader = new InputStreamReader(zip); JSONObject jo = (JSONObject)new JSONParser().parse(reader); String title = (String)jo.get("title"); - result.put(TITLE,title); + if(!StringUtils.isBlank(title)) { + result.put(TITLE, title); + } } } diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index bcfc7bc597..3e9d2a823e 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -35,7 +35,7 @@ "ngx-monaco-editor-v2": "^16.0.1", "ngx-slider-v2": "^16.0.2", "rxjs": "^6.5.4", - "tinymce": "^6.8.3", + "tinymce": "^7.2.1", "tinymce-i18n": "^23.9.11", "tslib": "^2.3.1", "uuid": "^3.3.2", @@ -8527,6 +8527,12 @@ "@angular/forms": ">=14.0.0" } }, + "node_modules/@tinymce/tinymce-angular/node_modules/tinymce": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.8.4.tgz", + "integrity": "sha512-okoJyxuPv1gzASxQDNgQbnUXOdAIyoOSXcXcZZu7tiW0PSKEdf3SdASxPBupRj+64/E3elHwVRnzSdo82Emqbg==", + "license": "MIT" + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -25197,9 +25203,10 @@ "dev": true }, "node_modules/tinymce": { - "version": "6.8.3", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.8.3.tgz", - "integrity": "sha512-3fCHKAeqT+xNwBVESf6iDbDV0VNwZNmfrkx9c/6Gz5iB8piMfaO6s7FvoiTrj1hf1gVbfyLTnz1DooI6DhgINQ==" + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.2.1.tgz", + "integrity": "sha512-ADd1cvdIuq6NWyii0ZOZRuu+9sHIdQfcRNWBcBps2K8vy7OjlRkX6iw7zz1WlL9kY4z4L1DvIP+xOrVX/46aHA==", + "license": "GPL-2.0-or-later" }, "node_modules/tinymce-i18n": { "version": "23.9.11", @@ -33230,6 +33237,13 @@ "requires": { "tinymce": "^6.0.0 || ^5.5.0", "tslib": "^2.3.0" + }, + "dependencies": { + "tinymce": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.8.4.tgz", + "integrity": "sha512-okoJyxuPv1gzASxQDNgQbnUXOdAIyoOSXcXcZZu7tiW0PSKEdf3SdASxPBupRj+64/E3elHwVRnzSdo82Emqbg==" + } } }, "@tootallnate/once": { @@ -45881,9 +45895,9 @@ "dev": true }, "tinymce": { - "version": "6.8.3", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.8.3.tgz", - "integrity": "sha512-3fCHKAeqT+xNwBVESf6iDbDV0VNwZNmfrkx9c/6Gz5iB8piMfaO6s7FvoiTrj1hf1gVbfyLTnz1DooI6DhgINQ==" + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.2.1.tgz", + "integrity": "sha512-ADd1cvdIuq6NWyii0ZOZRuu+9sHIdQfcRNWBcBps2K8vy7OjlRkX6iw7zz1WlL9kY4z4L1DvIP+xOrVX/46aHA==" }, "tinymce-i18n": { "version": "23.9.11", diff --git a/Frontend/package.json b/Frontend/package.json index babd23892e..08ac7a2f05 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -55,7 +55,7 @@ "@angular/router": "^16.2.6", "@ngx-translate/core": "^14.0.0", "@tinymce/tinymce-angular": "^7.0.0", - "tinymce": "^6.8.3", + "tinymce": "^7.2.1", "tinymce-i18n": "^23.9.11", "angular-material-css-vars": "^5.0.2", "angular-resizable-element": "^7.0.2", diff --git a/Frontend/projects/edu-sharing-api/src/lib/api-request-configuration.ts b/Frontend/projects/edu-sharing-api/src/lib/api-request-configuration.ts index 5af4718a3d..0c0162f8b6 100644 --- a/Frontend/projects/edu-sharing-api/src/lib/api-request-configuration.ts +++ b/Frontend/projects/edu-sharing-api/src/lib/api-request-configuration.ts @@ -9,7 +9,8 @@ import { ApiConfiguration } from './api/api-configuration'; @Injectable() export class ApiRequestConfiguration { private authForNextRequest: string | null = null; - private locale: string | null = null; + private _locale: string | null = null; + private _language: string | null = null; /** Emits each time, an API request is performed. */ readonly apiRequest = new EventEmitter(); @@ -17,7 +18,22 @@ export class ApiRequestConfiguration { constructor(private apiConfiguration: ApiConfiguration) {} setLocale(locale: string): void { - this.locale = locale; + this._locale = locale; + } + + /** + * internal edu sharing language code, might be "de", "en" or something like "de-formal" + */ + setLanguage(language: string): void { + this._language = language; + } + + public getLocale(): string | null { + return this._locale; + } + + public getLanguage(): string | null { + return this._language; } setBasicAuthForNextRequest(auth: { username: string; password: string }): void { @@ -47,8 +63,11 @@ export class ApiRequestConfiguration { } this.apiRequest.emit(); headers['X-Client-Trace-Id'] = this.generateTraceId(); - if (this.locale) { - headers.locale = this.locale; + if (this._locale) { + headers.locale = this._locale; + } + if (this._language) { + headers['X-Edu-Sharing-Language'] = this._language; } if (this.authForNextRequest) { headers.Authorization = this.authForNextRequest; diff --git a/Frontend/projects/edu-sharing-api/src/lib/wrappers/config.service.ts b/Frontend/projects/edu-sharing-api/src/lib/wrappers/config.service.ts index 2eb68550c0..ea0fa4efe8 100644 --- a/Frontend/projects/edu-sharing-api/src/lib/wrappers/config.service.ts +++ b/Frontend/projects/edu-sharing-api/src/lib/wrappers/config.service.ts @@ -35,7 +35,10 @@ export const LANGUAGES: { [key: string]: Locale } = { }) export class ConfigService { private readonly updateTrigger = new Subject(); - private readonly localeSubject = new BehaviorSubject(null); + private readonly localeSubject = new BehaviorSubject<{ + locale: Locale; + language: string; + } | null>(null); private configSubject = new BehaviorSubject(undefined); private readonly config$ = this.updateTrigger.pipe( startWith(void 0 as void), @@ -51,9 +54,19 @@ export class ConfigService { private readonly defaultTranslations$ = this.localeSubject.pipe( filter((locale) => locale !== null), distinctUntilChanged(), - switchMap((locale) => of({ locale: locale, dict: this.configV1.getLanguageDefaults() })), + switchMap((locale) => + of({ + locale: locale?.locale, + language: locale?.language, + dict: this.configV1.getLanguageDefaults(), + }), + ), shareReplay(1), - ) as unknown as Observable<{ locale: Locale; dict: Observable }>; + ) as unknown as Observable<{ + locale: Locale; + language: string; + dict: Observable; + }>; private readonly translationOverrides$ = this.localeSubject.pipe( filter((locale) => locale !== null), distinctUntilChanged(), @@ -99,10 +112,14 @@ export class ConfigService { * * This affects translations and localized MDS widget values. */ - setLocale(locale: Locale): void { + setLocale(locale: Locale, language: string): void { if (locale) { this.apiRequestConfiguration.setLocale(locale); - this.localeSubject.next(locale); + this.apiRequestConfiguration.setLanguage(language); + this.localeSubject.next({ + locale: locale, + language: language, + }); } else { console.warn('Called `setLocale` with undefined `locale`'); } @@ -117,6 +134,7 @@ export class ConfigService { */ observeDefaultTranslations(): Observable<{ locale: Locale; + language: string; dict: Observable; }> { return this.defaultTranslations$; diff --git a/Frontend/projects/edu-sharing-ui/src/lib/node-entries/drag-preview/drag-preview.component.html b/Frontend/projects/edu-sharing-ui/src/lib/node-entries/drag-preview/drag-preview.component.html index b3ace66e38..a240b470f8 100644 --- a/Frontend/projects/edu-sharing-ui/src/lib/node-entries/drag-preview/drag-preview.component.html +++ b/Frontend/projects/edu-sharing-ui/src/lib/node-entries/drag-preview/drag-preview.component.html @@ -1,6 +1,6 @@
- +
diff --git a/Frontend/projects/edu-sharing-ui/src/lib/node-entries/node-entries-table/node-entries-table.component.html b/Frontend/projects/edu-sharing-ui/src/lib/node-entries/node-entries-table/node-entries-table.component.html index 4ab3e0065e..3396352e2b 100644 --- a/Frontend/projects/edu-sharing-ui/src/lib/node-entries/node-entries-table/node-entries-table.component.html +++ b/Frontend/projects/edu-sharing-ui/src/lib/node-entries/node-entries-table/node-entries-table.component.html @@ -157,10 +157,6 @@ attribute: column }) " - esCheckTextOverflow - #text="esCheckTextOverflow" - [matTooltip]="text.hasTextOverflow() ? cell.innerText : null" - matTooltipTouchGestures="off" >
diff --git a/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.spec.ts b/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.spec.ts index 0a215db4e5..8d3719cd16 100644 --- a/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.spec.ts +++ b/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.spec.ts @@ -21,11 +21,12 @@ class ConfigStub { } observeDefaultTranslations(lang: string): Observable<{ locale: Locale; + language: string; dict: Observable; }> { return rxjs.of(null); } - setLocale(lang: string): void {} + setLocale(locale: Locale, language: string): void {} } describe('TranslationLoader', () => { @@ -103,7 +104,7 @@ describe('TranslationLoader', () => { it('should call setLocale with correct locale', async () => { const setLocaleSpy = spyOn(config, 'setLocale').and.callThrough(); await callGetTranslation('de'); - expect(setLocaleSpy.calls.mostRecent().args).toEqual(['de_DE']); + expect(setLocaleSpy.calls.mostRecent().args).toEqual(['de_DE', 'de']); }); it('should not call observeDefaultTranslations', async () => { @@ -299,12 +300,16 @@ describe('TranslationLoader', () => { it('should call setLocale with correct locale', async () => { const setLocaleSpy = spyOn(config, 'setLocale').and.callThrough(); await callGetTranslation('de'); - expect(setLocaleSpy.calls.mostRecent().args).toEqual(['de_DE']); + expect(setLocaleSpy.calls.mostRecent().args).toEqual(['de_DE', 'de']); }); it('should include translations via observeDefaultTranslations', async () => { - config.observeDefaultTranslations = (lang) => - rxjs.of({ locale: LANGUAGES['de'], dict: rxjs.of({ foo: 'bar' }) }); + config.observeDefaultTranslations = (language) => + rxjs.of({ + locale: LANGUAGES[language], + language, + dict: rxjs.of({ foo: 'bar' }), + }); const result = await callGetTranslation('de'); expect(result).toEqual({ foo: 'bar' }); }); @@ -318,8 +323,12 @@ describe('TranslationLoader', () => { }); it('should merge translations via observeTranslationOverrides', async () => { - config.observeDefaultTranslations = (lang) => - rxjs.of({ locale: LANGUAGES['de'], dict: rxjs.of({ foo: 'bar' }) }); + config.observeDefaultTranslations = (language) => + rxjs.of({ + locale: LANGUAGES[language], + language, + dict: rxjs.of({ foo: 'bar' }), + }); config.observeTranslationOverrides = (lang) => { return rxjs.of({ bar: 'baz' }); }; @@ -328,8 +337,12 @@ describe('TranslationLoader', () => { }); it('should override nested translations via observeTranslationOverrides', async () => { - config.observeDefaultTranslations = (lang) => - rxjs.of({ locale: LANGUAGES['de'], dict: rxjs.of({ prefix: { foo: 'bar' } }) }); + config.observeDefaultTranslations = (language) => + rxjs.of({ + locale: LANGUAGES[language], + language, + dict: rxjs.of({ prefix: { foo: 'bar' } }), + }); config.observeTranslationOverrides = (lang) => { return rxjs.of({ prefix: { bar: 'baz' } }); }; @@ -338,8 +351,12 @@ describe('TranslationLoader', () => { }); it('should deep-merge translations via observeTranslationOverrides', async () => { - config.observeDefaultTranslations = (lang) => - rxjs.of({ locale: LANGUAGES['de'], dict: rxjs.of({ prefix: { foo: 'bar' } }) }); + config.observeDefaultTranslations = (language) => + rxjs.of({ + locale: LANGUAGES[language], + language, + dict: rxjs.of({ prefix: { foo: 'bar' } }), + }); config.observeTranslationOverrides = (lang) => rxjs.of({ 'prefix.bar': 'baz' }); const result = await callGetTranslation('de'); expect(result).toEqual({ prefix: { foo: 'bar', bar: 'baz' } }); diff --git a/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.ts b/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.ts index a8cd41c131..545e7f96bc 100644 --- a/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.ts +++ b/Frontend/projects/edu-sharing-ui/src/lib/translations/translation-loader.ts @@ -59,11 +59,12 @@ export class TranslationLoader implements TranslateLoader { if (lang === 'none') { return of({}); } - if (!LANGUAGES[lang]) { - console.error('unknown locale for language ' + lang); + // backend can not handle sub-languages + const langBackend = lang.startsWith('de-') ? 'de' : lang; + if (!LANGUAGES[langBackend]) { + console.error('unknown locale for language ' + lang + ' / ' + langBackend); } - console.log('lang', lang); - this.configService.setLocale(LANGUAGES[lang]); + this.configService.setLocale(LANGUAGES[langBackend], lang); return rxjs .forkJoin({ originalTranslations: this.getOriginalTranslations(lang).pipe( @@ -91,7 +92,7 @@ export class TranslationLoader implements TranslateLoader { switch (this.getSource()) { case 'repository': return this.configService.observeDefaultTranslations().pipe( - filter((arg) => !arg || arg.locale === LANGUAGES[lang]), + filter((arg) => !arg?.locale || arg.language === lang), switchMap((arg) => arg?.dict?.pipe(first()) || of(null)), first(), ); diff --git a/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.spec.ts b/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.spec.ts index cdbbb1dff9..b259119cf2 100644 --- a/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.spec.ts +++ b/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.spec.ts @@ -65,6 +65,8 @@ describe('TranslationsService', () => { sessionStorageServiceStub as any as SessionStorageService, translateServiceSpy as TranslateService, null, + null, + null, ); }); diff --git a/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.ts b/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.ts index 47cd83abc0..140db27923 100644 --- a/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.ts +++ b/Frontend/projects/edu-sharing-ui/src/lib/translations/translations.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Optional } from '@angular/core'; +import { ApplicationRef, Injectable, Optional } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, from, Observable, of as observableOf } from 'rxjs'; @@ -27,8 +27,19 @@ export class TranslationsService { private route: ActivatedRoute, private storage: SessionStorageService, private translate: TranslateService, + private sessionStorage: SessionStorageService, + private ref: ApplicationRef, @Optional() private appService: AppService, - ) {} + ) { + this.sessionStorage?.observe('language').subscribe((lang) => { + // language has changed, i.e. user has different preference + if (this.translate.currentLang && this.translate.currentLang !== lang) { + this.initialize().subscribe(() => { + this.ref.tick(); + }); + } + }); + } /** * Determines and configures the language to use and triggers loading of translations with diff --git a/Frontend/src/app/core-ui-module/styles/material-theme.scss b/Frontend/src/app/core-ui-module/styles/material-theme.scss index 0856107fea..e01ec727a8 100644 --- a/Frontend/src/app/core-ui-module/styles/material-theme.scss +++ b/Frontend/src/app/core-ui-module/styles/material-theme.scss @@ -340,12 +340,23 @@ body { .mat-mdc-checkbox { .mdc-checkbox__background { border-color: $primary !important; + background-color: #fff !important; .mdc-checkbox__checkmark { color: #fff !important; } } + &.mdc-checkbox--disabled { + .mdc-checkbox__background { + border-color: $primaryLight !important; + } + &.mat-mdc-checkbox-checked { + .mdc-checkbox__background { + background-color: $primaryLight !important; + } + } + } &.mat-mdc-checkbox-checked { .mdc-checkbox__background { background-color: $primary !important; diff --git a/Frontend/src/app/features/dialogs/dialog-modules/add-with-connector-dialog/add-with-connector-dialog.component.html b/Frontend/src/app/features/dialogs/dialog-modules/add-with-connector-dialog/add-with-connector-dialog.component.html index e9420d285b..b919570adc 100644 --- a/Frontend/src/app/features/dialogs/dialog-modules/add-with-connector-dialog/add-with-connector-dialog.component.html +++ b/Frontend/src/app/features/dialogs/dialog-modules/add-with-connector-dialog/add-with-connector-dialog.component.html @@ -11,10 +11,9 @@ 'CONNECTOR.MIMETYPE.' + (filetype.mimetype === 'application/zip' ? filetype.ccressourcetype + - '_' + - filetype.ccressourceversion + - '_' + - filetype.ccresourcesubtype + (filetype.ccressourceversion + ? '_' + filetype.ccressourceversion + '_' + filetype.ccresourcesubtype + : '') : filetype.mimetype) | translate }} diff --git a/Frontend/src/app/features/dialogs/dialog-modules/mds-editor-dialog/mds-editor-dialog.component.ts b/Frontend/src/app/features/dialogs/dialog-modules/mds-editor-dialog/mds-editor-dialog.component.ts index 40a78d3842..234944a468 100644 --- a/Frontend/src/app/features/dialogs/dialog-modules/mds-editor-dialog/mds-editor-dialog.component.ts +++ b/Frontend/src/app/features/dialogs/dialog-modules/mds-editor-dialog/mds-editor-dialog.component.ts @@ -44,7 +44,12 @@ export class MdsEditorDialogComponent implements OnInit, AfterViewInit { } async ngOnInit(): Promise { - await this.initMdsEditor(); + try { + await this.initMdsEditor(); + } catch (e) { + this.handleError(e); + this.dialogRef.close(null); + } this.initButtons(); this.registerProgressIndicator(); // `SendFeedbackDialog` works similar to this component. Please update accordingly when @@ -180,7 +185,7 @@ export class MdsEditorDialogComponent implements OnInit, AfterViewInit { } private handleError(error: any): void { console.error(error); - if (error instanceof UserPresentableError) { + if (error instanceof UserPresentableError || error.message) { this.toast.error(null, error.message); } else { this.toast.error(error); diff --git a/Frontend/src/app/features/dialogs/dialog-modules/simple-edit-dialog/simple-edit-license/simple-edit-license.component.ts b/Frontend/src/app/features/dialogs/dialog-modules/simple-edit-dialog/simple-edit-license/simple-edit-license.component.ts index 6546c5b84f..9e7da0cc61 100644 --- a/Frontend/src/app/features/dialogs/dialog-modules/simple-edit-dialog/simple-edit-license/simple-edit-license.component.ts +++ b/Frontend/src/app/features/dialogs/dialog-modules/simple-edit-dialog/simple-edit-license/simple-edit-license.component.ts @@ -174,7 +174,7 @@ export class SimpleEditLicenseComponent implements OnInit { this.initalAuthorFreetext = this.authorFreetext; setTimeout(async () => { if (updateInvalid) { - this.invalid = !this.fromUpload && !isValid; + this.invalid = !isValid; this.wasInvalid = this.invalid; } if (this.tpLicense && this.modeGroup) { diff --git a/Frontend/src/app/features/mds/mds-editor/mds-editor-core/mds-editor-core.component.scss b/Frontend/src/app/features/mds/mds-editor/mds-editor-core/mds-editor-core.component.scss index 8d571b586a..976fc9bf5b 100644 --- a/Frontend/src/app/features/mds/mds-editor/mds-editor-core/mds-editor-core.component.scss +++ b/Frontend/src/app/features/mds/mds-editor/mds-editor-core/mds-editor-core.component.scss @@ -6,4 +6,12 @@ // // An alternative would be to use `overflow-x: clip` when it is supported by Safari. padding: 0 var(--mds-editor-horizontal-padding, 0); + ::ng-deep { + // fix multi-line bottom hints + .mat-mdc-form-field-hint-wrapper, + .mat-mdc-form-field-error-wrapper { + position: relative; + margin-top: -20px; + } + } } diff --git a/Frontend/src/app/features/mds/mds-editor/mds-editor-instance.service.ts b/Frontend/src/app/features/mds/mds-editor/mds-editor-instance.service.ts index 4b85c848e3..25618f44db 100644 --- a/Frontend/src/app/features/mds/mds-editor/mds-editor-instance.service.ts +++ b/Frontend/src/app/features/mds/mds-editor/mds-editor-instance.service.ts @@ -262,7 +262,10 @@ export class MdsEditorInstanceService implements OnDestroy { } // Set initial values, so the initial completion status is calculated correctly. this.value$.next([...this.initialValues.jointValues]); - if (this.mdsEditorInstanceService.getIsBulk(nodes)) { + if ( + this.mdsEditorInstanceService.getIsBulk(nodes) && + this.bulkMode.value !== 'replace' + ) { this.bulkMode.next('no-change'); } this.initialValuesSubject.next(this.initialValues); diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-checkbox/mds-editor-widget-checkbox.component.ts b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-checkbox/mds-editor-widget-checkbox.component.ts index 88afbc43f5..d486fa68da 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-checkbox/mds-editor-widget-checkbox.component.ts +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-checkbox/mds-editor-widget-checkbox.component.ts @@ -12,17 +12,19 @@ export class MdsEditorWidgetCheckboxComponent extends MdsEditorWidgetBase implem formControl: UntypedFormControl; isIndeterminate: boolean; - ngOnInit(): void { - const initialValue = this.widget.getInitialValues()?.jointValues?.[0]; - this.isIndeterminate = !!this.widget.getInitialValues()?.individualValues; - this.setIndeterminateValues(this.isIndeterminate); + async ngOnInit() { this.formControl = new UntypedFormControl( - initialValue === 'true', + false, this.getStandardValidators({ requiredValidator }), ); this.formControl.valueChanges.subscribe((value: boolean) => { this.setValue([value ? value.toString() : 'false'], this.formControl.dirty); }); + const initialValue = (await this.widget.getInitalValuesAsync()).jointValues?.[0]; + this.formControl.setValue(initialValue === 'true'); + this.isIndeterminate = !!(await this.widget.getInitalValuesAsync()).individualValues; + this.setIndeterminateValues(this.isIndeterminate); + this.registerValueChanges(this.formControl); } diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-chips/mds-editor-widget-chips.component.scss b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-chips/mds-editor-widget-chips.component.scss index a0360a273f..e14ee4adc7 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-chips/mds-editor-widget-chips.component.scss +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-chips/mds-editor-widget-chips.component.scss @@ -23,7 +23,17 @@ mat-form-field { // spacing for drop-down button padding-right: 35px; } - +mat-chip { + &.indeterminate { + background: repeating-linear-gradient( + -45deg, + var(--palette-primary-50), + var(--palette-primary-50) 5px, + var(--palette-primary-100) 5px, + var(--palette-primary-100) 10px + ); + } +} .mat-select-arrow { // Use the button color which respects disabled state. color: unset; diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.html b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.html index 1c7d61e153..c0aed4fc1b 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.html +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.html @@ -56,6 +56,7 @@

- - {{ widget.definition.bottomCaption }} - + {{ + widget.definition.bottomCaption + }} diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.ts b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.ts index edcdc4ff37..52f8b5602c 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.ts +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-container/mds-editor-widget-container.component.ts @@ -79,6 +79,10 @@ export class MdsEditorWidgetContainerComponent @Input() injectedView: MdsEditorWidgetBase | NativeWidgetComponent; @Input() valueType: ValueType; @Input() label: string | boolean; + /*+ + display the bottom caption, if present on the widget + */ + @Input() showBottomCaption = true; @Input() control: AbstractControl; /** * Whether to wrap in a `mat-form-field`. diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-radio-button/mds-editor-widget-radio-button.component.scss b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-radio-button/mds-editor-widget-radio-button.component.scss index 3b02e123e2..6f684f1f43 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-radio-button/mds-editor-widget-radio-button.component.scss +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-radio-button/mds-editor-widget-radio-button.component.scss @@ -1,18 +1,21 @@ $button-margin-horizontal: 20px; $button-margin-vertical: 10px; -.mat-radio-group { +mat-radio-group { display: flex; flex-wrap: wrap; margin-right: -$button-margin-horizontal; - margin-bottom: -$button-margin-vertical; padding: 5px 20px; &.vertical { flex-direction: column; } } -.mat-radio-button { +mat-radio-button.mat-mdc-radio-button { margin-right: $button-margin-horizontal; - margin-bottom: $button-margin-vertical; + ::ng-deep { + .mdc-radio { + --mdc-radio-state-layer-size: 32px; + } + } } diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-select/mds-editor-widget-select.component.ts b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-select/mds-editor-widget-select.component.ts index 57c943156c..7a915f1722 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-select/mds-editor-widget-select.component.ts +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-select/mds-editor-widget-select.component.ts @@ -4,6 +4,7 @@ import { MatTooltip } from '@angular/material/tooltip'; import { MdsWidget, MdsWidgetValue } from '../../../types/types'; import { MdsEditorWidgetBase, ValueType } from '../mds-editor-widget-base'; import { MatSelect } from '@angular/material/select'; +import { skip } from 'rxjs/operators'; @Component({ selector: 'es-mds-editor-widget-select', @@ -45,10 +46,15 @@ export class MdsEditorWidgetSelectComponent extends MdsEditorWidgetBase implemen ); } }); + this.formControl.valueChanges.subscribe((value) => + this.setValue(value ? [value.id] : [null]), + ); + } else { + // skip first because the init state will cause a trigger + this.formControl.valueChanges + .pipe(skip(1)) + .subscribe((value) => this.setValue(value ? [value.id] : [null])); } - this.formControl.valueChanges.subscribe((value) => { - this.setValue(value ? [value.id] : [null]); - }); this.registerValueChanges(this.formControl); } diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-text/mds-editor-widget-text.component.html b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-text/mds-editor-widget-text.component.html index 59d4a48247..5f5b8e3252 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-text/mds-editor-widget-text.component.html +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-text/mds-editor-widget-text.component.html @@ -36,7 +36,9 @@ [injectedView]="this" [widget]="widget" [label]="false" + [control]="formControl" [wrapInFormField]="false" + [showBottomCaption]="false" [valueType]="valueType" > @@ -63,12 +65,12 @@ (blur)="blur()" (keydown.enter)="submit()" > - - {{ widget.definition.bottomCaption }} - - - - + {{ + widget.definition.bottomCaption + }} +

diff --git a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-wysiwyg-html/mds-editor-widget-tinymce.component.ts b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-wysiwyg-html/mds-editor-widget-tinymce.component.ts index a0dc0440b1..12c4756488 100644 --- a/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-wysiwyg-html/mds-editor-widget-tinymce.component.ts +++ b/Frontend/src/app/features/mds/mds-editor/widgets/mds-editor-widget-wysiwyg-html/mds-editor-widget-tinymce.component.ts @@ -84,6 +84,8 @@ export class MdsEditorWidgetTinyMCE extends MdsEditorWidgetBase implements OnIni async ngAfterViewInit() { this._html = (await this.widget.getInitalValuesAsync()).jointValues[0]; + (this.editorConfigDefault as any).base_url = + this.platformLocation.getBaseHrefFromDOM() + 'tinymce/'; if (this.widget.definition.configuration) { this.editorConfig = { ...this.editorConfigDefault, diff --git a/Frontend/src/app/main/error-handler.service.ts b/Frontend/src/app/main/error-handler.service.ts index 1784b2bc09..1f58d5c16f 100644 --- a/Frontend/src/app/main/error-handler.service.ts +++ b/Frontend/src/app/main/error-handler.service.ts @@ -24,7 +24,11 @@ export class ErrorHandlerService { async handleError(error: ApiErrorResponse, req: HttpRequest) { // console.log('handleError', error, req); - if (req.method === 'GET' && req.url === this.getApiUrl('/config/v1/values')) { + if (error?.error?.type === 'abort') { + // this might cause by explicit abort and a redirect + // do nothing to prevent firefox from showing a temporary message i.e. on login sso redirect + console.warn('Explicitly aborted, do not displaying a message', error); + } else if (req.method === 'GET' && req.url === this.getApiUrl('/config/v1/values')) { this.showConfigurationErrorNotice(error); } else if ( req.method === 'GET' && diff --git a/Frontend/src/app/main/navigation/main-menu-dropdown/main-menu-dropdown.component.scss b/Frontend/src/app/main/navigation/main-menu-dropdown/main-menu-dropdown.component.scss index b66d9667d6..d66ad8a082 100644 --- a/Frontend/src/app/main/navigation/main-menu-dropdown/main-menu-dropdown.component.scss +++ b/Frontend/src/app/main/navigation/main-menu-dropdown/main-menu-dropdown.component.scss @@ -22,18 +22,20 @@ $mainMenuSelectedBackground: linear-gradient( @if variable-exists(mainMenuBackgroundColor) { background-color: $mainMenuBackgroundColor; } - .mat-menu-item { + .mat-mdc-menu-item { @if variable-exists(mainMenuForegroundColor) { &:not(:disabled) { - color: $mainMenuForegroundColor; - @if variable-exists(mainMenuForegroundColorIcon) { - > i { - color: $mainMenuForegroundColorIcon; + > span { + color: $mainMenuForegroundColor; + @if variable-exists(mainMenuForegroundColorIcon) { + > i { + color: $mainMenuForegroundColorIcon; + } } } } } - &.mat-menu-item-selected { + &.mat-mdc-menu-item-selected { background: $mainMenuSelectedBackground; } &.cdk-focused { @@ -45,10 +47,12 @@ $mainMenuSelectedBackground: linear-gradient( &.cdk-key-focused { background-color: $mainMenuHoverBackgroundColor; @if variable-exists(mainMenuBackgroundColor) { - color: $mainMenuHoverForegroundColor; - @if variable-exists(mainMenuHoverForegroundColorIcon) { - > i { - color: $mainMenuHoverForegroundColorIcon; + > span { + color: $mainMenuHoverForegroundColor; + @if variable-exists(mainMenuHoverForegroundColorIcon) { + > i { + color: $mainMenuHoverForegroundColorIcon; + } } } } diff --git a/Frontend/src/app/main/navigation/top-bar/top-bar.component.html b/Frontend/src/app/main/navigation/top-bar/top-bar.component.html index 1568a48f56..4e16b16c94 100644 --- a/Frontend/src/app/main/navigation/top-bar/top-bar.component.html +++ b/Frontend/src/app/main/navigation/top-bar/top-bar.component.html @@ -160,7 +160,7 @@ data-test="main-nav-user-menu-button" > - {{ currentUser | authorityName }} + {{ currentUser | nodePersonName }} {{ 'USER_GUEST' | translate }} diff --git a/Frontend/src/app/main/navigation/top-bar/top-bar.component.scss b/Frontend/src/app/main/navigation/top-bar/top-bar.component.scss index e9e6e1e182..851330b095 100644 --- a/Frontend/src/app/main/navigation/top-bar/top-bar.component.scss +++ b/Frontend/src/app/main/navigation/top-bar/top-bar.component.scss @@ -47,6 +47,7 @@ es-imprint-privacy ::ng-deep { border: none; outline: none; background-color: unset; + font-family: $primaryFontFamily; color: $workspaceTopBarFontColor !important; & ::ng-deep .mat-ripple-element { background-color: #{color.change($workspaceTopBarFontColor, $alpha: 0.1)}; @@ -97,6 +98,7 @@ es-imprint-privacy ::ng-deep { .menuButton img { margin-left: 0; width: 35px; + margin-right: 5px; } span { diff --git a/Frontend/src/app/pages/admin-page/admin-page.component.html b/Frontend/src/app/pages/admin-page/admin-page.component.html index 88a0f9fc39..9e749e1e58 100644 --- a/Frontend/src/app/pages/admin-page/admin-page.component.html +++ b/Frontend/src/app/pages/admin-page/admin-page.component.html @@ -821,12 +821,6 @@
{{ 'ADMIN.JOBS.PARAMETERS' | translate }}
{{ 'ADMIN.TOOLKIT.AUTHENTICATE' | translate }}
-
-
{{ 'ADMIN.TOOLKIT.REFRESH_EDU_GROUP_CACHE' | translate }}
- -
cached{{ 'ADMIN.TOOLKIT.CACHE' | translate }}
diff --git a/Frontend/src/app/pages/admin-page/admin-page.component.ts b/Frontend/src/app/pages/admin-page/admin-page.component.ts index 2c7a5705e2..5d65493001 100644 --- a/Frontend/src/app/pages/admin-page/admin-page.component.ts +++ b/Frontend/src/app/pages/admin-page/admin-page.component.ts @@ -602,19 +602,7 @@ export class AdminPageComponent implements OnInit, OnDestroy { }, ); } - public refreshEduGroupCache() { - this.globalProgress = true; - this.admin.refreshEduGroupCache().subscribe( - () => { - this.globalProgress = false; - this.toast.toast('ADMIN.TOOLKIT.EDU_GROUP_CACHE_REFRESHED'); - }, - (error: any) => { - this.globalProgress = false; - this.toast.error(error); - }, - ); - } + public refreshCache(sticky: boolean) { this.globalProgress = true; this.admin diff --git a/Frontend/src/app/pages/render-page/render-page.component.ts b/Frontend/src/app/pages/render-page/render-page.component.ts index 25189c4967..c1e96e4fe0 100644 --- a/Frontend/src/app/pages/render-page/render-page.component.ts +++ b/Frontend/src/app/pages/render-page/render-page.component.ts @@ -776,7 +776,7 @@ export class RenderPageComponent implements EventListener, OnInit, OnDestroy, Af params.mds = this.getMdsId(); params.sidenav = true; params.repo = this.repository; - params.parameters = JSON.stringify(data); + params.filters = JSON.stringify(data); this.router.navigate([UIConstants.ROUTER_PREFIX + 'search'], { queryParams: params }); }); } diff --git a/Frontend/src/app/pages/search-page/search-page-results-all.component.html b/Frontend/src/app/pages/search-page/search-page-results-all.component.html index 017645b92c..c2eaf16cce 100644 --- a/Frontend/src/app/pages/search-page/search-page-results-all.component.html +++ b/Frontend/src/app/pages/search-page/search-page-results-all.component.html @@ -35,7 +35,7 @@

- {{ repo.title | titlecase }} + {{ repo.title }} ({{ repo.dataSource.getTotal() }}) diff --git a/Frontend/src/app/pages/workspace-page/workspace-page.component.ts b/Frontend/src/app/pages/workspace-page/workspace-page.component.ts index 6e0284d30a..237af19243 100644 --- a/Frontend/src/app/pages/workspace-page/workspace-page.component.ts +++ b/Frontend/src/app/pages/workspace-page/workspace-page.component.ts @@ -332,9 +332,9 @@ export class WorkspacePageComponent implements EventListener, OnInit, OnDestroy handleDrop(event: { target: DropTarget; source: DropSource }) { if (event.source.mode === 'copy') { - this.copyNode(event.target, event.source.element); + this.copyNode(event.target, event.source.element?.slice()); } else { - this.moveNode(event.target, event.source.element); + this.moveNode(event.target, event.source.element?.slice()); } /* this.dialogTitle="WORKSPACE.DRAG_DROP_TITLE"; diff --git a/Frontend/src/app/shared/components/collection-chooser/collection-chooser.component.ts b/Frontend/src/app/shared/components/collection-chooser/collection-chooser.component.ts index 131dd28d0f..0415c93cd4 100644 --- a/Frontend/src/app/shared/components/collection-chooser/collection-chooser.component.ts +++ b/Frontend/src/app/shared/components/collection-chooser/collection-chooser.component.ts @@ -46,6 +46,7 @@ export class CollectionChooserComponent implements OnInit { readonly NodeEntriesDisplayType = NodeEntriesDisplayType; readonly InteractionType = InteractionType; readonly COLLECTION_LATEST_DEFAULT_COUNT = 3; + readonly COLLECTION_MY_MAX_COUNT = 100; @ContentChild('beforeRecent') beforeRecentRef: TemplateRef; /** * The caption of the dialog, will be translated automatically @@ -254,7 +255,7 @@ export class CollectionChooserComponent implements OnInit { { sortBy: this.sortBy, sortAscending: false, - count: RestConstants.COUNT_UNLIMITED, + count: this.COLLECTION_MY_MAX_COUNT, }, ) .subscribe((data) => { diff --git a/Frontend/src/assets/i18n/admin/de.json b/Frontend/src/assets/i18n/admin/de.json index 55dabd4706..117fe489e6 100644 --- a/Frontend/src/assets/i18n/admin/de.json +++ b/Frontend/src/assets/i18n/admin/de.json @@ -305,8 +305,6 @@ "TOOLKIT": { "REFRESH_APP": "App Info neu laden", "REFRESH_APP_INFO": "Metadaten und Application-Info neu laden", - "REFRESH_EDU_GROUP_CACHE": "Edu-Gruppen-Cache aktualisieren", - "EDU_GROUP_CACHE_REFRESHED": "Edu-Gruppen-Cache aktualisiert", "APP_INFO_REFRESHED": "Aktualisieren der Anwendungsinformationen abgeschlossen", "CACHE": "Cache Verwaltung", "CACHE_INFO": "Cache Info abrufen", diff --git a/Frontend/src/assets/i18n/admin/en.json b/Frontend/src/assets/i18n/admin/en.json index c35af891cd..61c56d66dd 100644 --- a/Frontend/src/assets/i18n/admin/en.json +++ b/Frontend/src/assets/i18n/admin/en.json @@ -297,8 +297,6 @@ "TOOLKIT": { "REFRESH_APP": "Reload App Info", "REFRESH_APP_INFO": "Reload metadata and application info", - "REFRESH_EDU_GROUP_CACHE": "Refresh Edu Group Cache", - "EDU_GROUP_CACHE_REFRESHED": "Refresh Edu Group Cache done", "APP_INFO_REFRESHED": "Refresh Application Info done", "CACHE": "Cache management", "CACHE_INFO": "Get Cache Info", diff --git a/Frontend/src/assets/i18n/admin/fr.json b/Frontend/src/assets/i18n/admin/fr.json index 5b69856605..b8fef602c1 100644 --- a/Frontend/src/assets/i18n/admin/fr.json +++ b/Frontend/src/assets/i18n/admin/fr.json @@ -294,8 +294,6 @@ "TOOLKIT": { "REFRESH_APP": "Reload App Info", "REFRESH_APP_INFO": "Recharger les métadonnées et les informations sur l'application", - "REFRESH_EDU_GROUP_CACHE": "Rafraîchir le cache du groupe Edu", - "EDU_GROUP_CACHE_REFRESHED": "Rafraîchir le cache du groupe Edu fait", "APP_INFO_REFRESHED": "Rafraîchir les informations sur l'application fait", "CACHE": "Gestion du cache", "CACHE_INFO": "Obtenir des informations sur le cache", @@ -621,7 +619,7 @@ "TOKEN": "Jeton", "TS": "Date d'expiration", "GENERATE": "Ajouter un jeton", - "SAVE": "Économiser", + "SAVE": "Sauver", "REMOVE_TITLE": "Supprimer le jeton ?", "REMOVE_MESSAGE": "Voulez-vous vraiment supprimer le jeton avec l'url \"{{url}}\" ?\n\nLes liens existants peuvent devenir indisponibles en raison de ce processus.", "DYNAMIC_INFO": "Avec l'enregistrement dynamique lti, vous pouvez interconnecter edu-sharing avec d'autres systèmes compatibles lti. Cliquez sur \"Generate Token\" pour générer un nouveau jeton url.\n\nInsérez-le dans votre système compatible lti (par exemple moodle). Le système externe sera ensuite automatiquement connecté.", diff --git a/Frontend/src/assets/i18n/admin/it.json b/Frontend/src/assets/i18n/admin/it.json index 1e98627a3d..2883024612 100644 --- a/Frontend/src/assets/i18n/admin/it.json +++ b/Frontend/src/assets/i18n/admin/it.json @@ -294,8 +294,6 @@ "TOOLKIT": { "REFRESH_APP": "Ricarica Info sull'app", "REFRESH_APP_INFO": "Ricarica i metadati e le informazioni sull'applicazione", - "REFRESH_EDU_GROUP_CACHE": "Aggiorna la cache del gruppo Edu", - "EDU_GROUP_CACHE_REFRESHED": "Aggiornare la cache del gruppo Edu", "APP_INFO_REFRESHED": "Aggiornare le informazioni sull'applicazione", "CACHE": "Gestione della cache", "CACHE_INFO": "Ottieni informazioni sulla cache", diff --git a/Frontend/src/assets/i18n/common/de-no-binnen-i.json b/Frontend/src/assets/i18n/common/de-no-binnen-i.json index d2353c720c..6a819f5e14 100644 --- a/Frontend/src/assets/i18n/common/de-no-binnen-i.json +++ b/Frontend/src/assets/i18n/common/de-no-binnen-i.json @@ -18,7 +18,7 @@ "WARNING": { "NO_LICENSE": "Dieses Material besitzt noch keine Lizenz.\nSetzen Sie sich ggf. mit der Autorin/dem Autor in Verbindung, um Lizenzverstöße zu vermeiden.", "COPYRIGHT": "Dieses Material besitzt eine Copyright-Lizenz.\nSetzen Sie sich ggf. mit der Autorin/dem Autor in Verbindung, um Lizenzverstöße zu vermeiden.", - "ND": "Dieses Material besitzt eine Non Derivative Lizenz. Diese Lizenz legt fest, dass das Material nicht verändert werden darf.\nSetzen Sie sich ggf. mit der AUtorin/dem Autor in Verbindung, um Lizenzverstöße zu vermeiden." + "ND": "Dieses Material besitzt eine Non Derivative Lizenz. Diese Lizenz legt fest, dass das Material nicht verändert werden darf.\nSetzen Sie sich ggf. mit der Autorin/dem Autor in Verbindung, um Lizenzverstöße zu vermeiden." } }, "FEEDBACK": { diff --git a/Frontend/src/assets/i18n/common/de.json b/Frontend/src/assets/i18n/common/de.json index 54dc54d653..05819ee9bb 100644 --- a/Frontend/src/assets/i18n/common/de.json +++ b/Frontend/src/assets/i18n/common/de.json @@ -615,6 +615,7 @@ "imsqti_xmlv2p1_test": "Test", "imsqti_xmlv2p1_item": "Aufgabe", "text/html": "HTML-Dokument (.html)", + "h5p": "H5P-Datei (.h5p)", "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Textdokument (.docx)", "application/vnd.oasis.opendocument.text": "Textdokument (.odt)", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Tabelle (.xlsx)", diff --git a/Frontend/src/assets/i18n/common/en.json b/Frontend/src/assets/i18n/common/en.json index 472649ba74..fa163107be 100644 --- a/Frontend/src/assets/i18n/common/en.json +++ b/Frontend/src/assets/i18n/common/en.json @@ -529,6 +529,7 @@ "imsqti_xmlv2p1_test": "Test", "imsqti_xmlv2p1_item": "Task", "text/html": "html document (.html)", + "h5p": "h5p file (.h5p)", "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "text document (.docx)", "application/vnd.oasis.opendocument.text": "text document (.odt)", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "spreadsheet (.xlsx)", diff --git a/Frontend/src/assets/i18n/messages/de-no-binnen-i.json b/Frontend/src/assets/i18n/messages/de-no-binnen-i.json index 128b879d7e..2067a0aead 100644 --- a/Frontend/src/assets/i18n/messages/de-no-binnen-i.json +++ b/Frontend/src/assets/i18n/messages/de-no-binnen-i.json @@ -1,6 +1,6 @@ { "MESSAGES": { - "share_file_deleted": "Die Linkfreigabe zeigt auf eine bereits gelöschte Datei. Bitte kontaktieren Sie den/die Bitte kontaktieren Sie die Autorin/den Autor", + "share_file_deleted": "Die Linkfreigabe zeigt auf eine bereits gelöschte Datei. Bitte kontaktieren Sie die Autorin/den Autor", "usage_missing": "Der Zugriff auf das Element ist fehlgeschlagen, da die Freigabe für den aktuellen Kontext entfernt wurde. Bitte wenden Sie sich an die Autorin/den Autorin der Seite, von welcher der Aufruf erfolgte" } } diff --git a/Frontend/src/assets/i18n/messages/de.json b/Frontend/src/assets/i18n/messages/de.json index 0c0663de4f..252f890ebd 100644 --- a/Frontend/src/assets/i18n/messages/de.json +++ b/Frontend/src/assets/i18n/messages/de.json @@ -13,6 +13,11 @@ "SHARING_ERROR": "Freigeben fehlgeschlagen", "LTI_ERROR": "LTI Prozess fehlgeschlagen", "LTI_REG_ERROR": "Anbindung der LTI Plattform fehlgeschlagen", + "USER_BLOCKED": "Login nicht möglich", + "SSO_INSUFFICIENT_REQUIREMENTS": "Login nicht möglich", + "SSO_UNKNOWN_ERROR": "Login nicht möglich", + "MISSING_PARAM": "Login nicht möglich", + "SSO_REQ_ATT_NOT_MATCHES": "Login nicht möglich", "DETAILS": { "403": "Der Zugriff auf den gewünschten Bereich bzw. die gewünschte Ressource ist leider nicht möglich", "404": "Fehler 404", @@ -25,7 +30,12 @@ "SHARING_ERROR": "edu-sharing kann aktuell leider nur Dateien begrenzter Größe teilen. Bitte versuchen Sie es mit einer anderen Datei", "LTI_ERROR": "Fehlermeldung: {{text}}", "LTI_REG_ERROR": "Fehlermeldung: {{text}}", - "INVALID": "Wenn dieser Fehler weiterhin auftritt, wenden Sie sich an den Betreiber des Systems" + "INVALID": "Wenn dieser Fehler weiterhin auftritt, wenden Sie sich an den Betreiber des Systems", + "USER_BLOCKED": "Benutzer gesperrt", + "SSO_INSUFFICIENT_REQUIREMENTS": "Vorraussetzungen nicht gegeben", + "SSO_UNKNOWN_ERROR": "unbekannter Fehler", + "MISSING_PARAM": "unvollständige Benutzerinformationen", + "SSO_REQ_ATT_NOT_MATCHES": "falsche Nutzerinformationen" } } } diff --git a/Frontend/src/assets/i18n/messages/en.json b/Frontend/src/assets/i18n/messages/en.json index caed6525e4..8618d19628 100644 --- a/Frontend/src/assets/i18n/messages/en.json +++ b/Frontend/src/assets/i18n/messages/en.json @@ -13,6 +13,11 @@ "LTI_ERROR": "LTI process failed", "LTI_REG_ERROR": "Connection to LTI platform failed", "SHARING_ERROR": "Sharing failed", + "USER_BLOCKED": "Login not possible", + "SSO_INSUFFICIENT_REQUIREMENTS": "Login not possible", + "SSO_UNKNOWN_ERROR": "Login not possible", + "MISSING_PARAM": "Login not possible", + "SSO_REQ_ATT_NOT_MATCHES": "Login not possible", "DETAILS": { "share_expired": "The shared link has expired or has been terminated by the owner of the material.", "share_file_deleted": "The shared link points to an object which has been deleted. Please contact the author.", @@ -25,7 +30,12 @@ "403": "The access to the given resource is restricted.", "404": "Error 404", "500": "Error 500", - "SHARING_ERROR": "Unfortunately, edu-sharing can currently only share files of a limited size. Please try with a different file" + "SHARING_ERROR": "Unfortunately, edu-sharing can currently only share files of a limited size. Please try with a different file", + "USER_BLOCKED": "User blocked", + "SSO_INSUFFICIENT_REQUIREMENTS": "Insufficient login requirements", + "MISSING_PARAM": "Incomplete user data", + "SSO_UNKNOWN_ERROR": "Unknown error", + "SSO_REQ_ATT_NOT_MATCHES": "wrong userdata" } } } diff --git a/Frontend/src/assets/i18n/permissions/de-no-binnen-i.json b/Frontend/src/assets/i18n/permissions/de-no-binnen-i.json index 3516a37bc4..62b3089706 100644 --- a/Frontend/src/assets/i18n/permissions/de-no-binnen-i.json +++ b/Frontend/src/assets/i18n/permissions/de-no-binnen-i.json @@ -27,10 +27,6 @@ "TAB": { "USER": "Nutzende Person" }, - "DELETE": { - "MAIN_HEADING": "Nutzende Person & Daten löschen", - "RECEIVER": "Nutzende Person suchen..." - }, "TOOLPERMISSIONS": { "EVERYONE_ALLOWED": "Ausgangswert für alle Nutzenden", @@ -38,6 +34,8 @@ }, "DELETE": { + "MAIN_HEADING": "Nutzende Person & Daten löschen", + "RECEIVER": "Nutzende Person suchen...", "CONFIRM": { "USERS": "Die Aktion wird für alle folgenden Nutzenden und deren gespeicherte Daten angewandt:", "RECEIVER_CAPTION": "Die gewählte Person wird als erstellende Person in der Daten & Sammlungen angezeigt" diff --git a/Frontend/src/assets/i18n/search/fr.json b/Frontend/src/assets/i18n/search/fr.json index cae6979eb1..dea9f8e8bc 100644 --- a/Frontend/src/assets/i18n/search/fr.json +++ b/Frontend/src/assets/i18n/search/fr.json @@ -25,7 +25,7 @@ "HIDE_EXPANDED_SEARCH": "Masquer la recherche étendue", "TOGGLE_RESULT_VIEW": "Vue alternée", "EMBED_SEARCH_ACTION": "Intégrer la recherche en cours", - "SAVE_SEARCH_ACTION": "Économiser", + "SAVE_SEARCH_ACTION": "Sauver", "FILTER": "Filtre", "FILTERS": "Filtres", "FILTER_BY": "Filtrer par : {{property}}", diff --git a/Frontend/src/assets/i18n/workspace/de-no-binnen-i.json b/Frontend/src/assets/i18n/workspace/de-no-binnen-i.json index b6cf94c395..27b087a0e0 100644 --- a/Frontend/src/assets/i18n/workspace/de-no-binnen-i.json +++ b/Frontend/src/assets/i18n/workspace/de-no-binnen-i.json @@ -22,9 +22,9 @@ }, "CONTRIBUTOR": { + "ADD_TITLE": "Neue beteiligte Person angeben", "TYPE": { "AUTHOR": "Autorin/Autor", - "ADD_TITLE": "Neue beteiligte Person angeben", "PUBLISHER": "Herausgeberschaft", "INITIATOR": "Auftrag", "GRAPHICAL_DESIGNER": "Grafik", diff --git a/config/defaults/pom.xml b/config/defaults/pom.xml index d3a15b3715..e309d650cd 100644 --- a/config/defaults/pom.xml +++ b/config/defaults/pom.xml @@ -19,6 +19,15 @@ edu_sharing-community-repository-config-defaults + + + src/main/resources + + + src/main/templates + true + + diff --git a/config/defaults/src/main/resources/caches.properties b/config/defaults/src/main/resources/caches.properties index 9e950e6c62..25e5b9bf55 100644 --- a/config/defaults/src/main/resources/caches.properties +++ b/config/defaults/src/main/resources/caches.properties @@ -84,16 +84,6 @@ cache.eduSharingEditLockCache.maxIdleSeconds=0 cache.eduSharingEditLockCache.maxItems=120000 cache.eduSharingEditLockCache.timeToLiveSeconds=0 -cache.eduSharingEduGroupCache.eviction-policy=LRU -cache.eduSharingEduGroupCache.maxIdleSeconds=0 -cache.eduSharingEduGroupCache.maxItems=120000 -cache.eduSharingEduGroupCache.timeToLiveSeconds=0 - -cache.eduSharingEduGroupFolderCache.eviction-policy=LRU -cache.eduSharingEduGroupFolderCache.maxIdleSeconds=0 -cache.eduSharingEduGroupFolderCache.maxItems=120000 -cache.eduSharingEduGroupFolderCache.timeToLiveSeconds=0 - cache.eduSharingLightBendConfigCache.eviction-policy=LRU cache.eduSharingLightBendConfigCache.maxIdleSeconds=0 cache.eduSharingLightBendConfigCache.maxItems=1000 diff --git a/config/defaults/src/main/resources/edu-sharing.reference.conf b/config/defaults/src/main/resources/edu-sharing.reference.conf index 21a7b80dab..61704d7b76 100644 --- a/config/defaults/src/main/resources/edu-sharing.reference.conf +++ b/config/defaults/src/main/resources/edu-sharing.reference.conf @@ -8,6 +8,26 @@ // security and management functions security { + access: { + // whether openapi access is allowed. possible values: "disabled", "admin", "enabled" + openapi: "enabled" + // config for rest endpoint + // note: The list works with prefixes, so "/register" would disable all register endpoints + // You can choose the state "disabled" or "admin" + endpoints: { + // "/statistic/v1/public": "admin" + // "/statistic/v1/facets": "admin" + // "/stream/v1": "admin" + // "/relation/v1": "admin" + // "/register/v1": "admin" + // "/rating/v1": "admin" + // "/notification/v1": "admin" + // "/knowledge/v1": "admin" + // "/feedback/v1": "admin" + // "/comment/v1": "admin" + // "/clientUtils/v1": "admin" + } + } fileManagement { limits: { // max allowed size of files that can be uploaded (in bytes) @@ -133,6 +153,11 @@ security { // allow authentication from lti connected plattforms enabled: false } + + external: { + //allow upstream service provider like apache shibd + enabled: false + } } } @@ -184,10 +209,20 @@ repository: { // // id of the group (GROUP_ prefix is not necessary) // id: "MY_GROUP" // // display name of the group - // displayName: ["My group name"] + // displayName: "My group name" // } ] } + // configuration for default user + persons: [ + // { + // authorityName: "MMiller", + // profile: {firstName:"Mike", lastName:"Miller"} + // password: "1234", + // primaryAffiliation: "teacher", + // groups: ["GROUP_A"] + // } + ] // configuration for toolpermissions toolpermissions: { // managed toolpermissions @@ -552,6 +587,8 @@ importer:{ exporter:{ oai:{ + // when disabled, the oai endpoint will return a forbidden status + enabled: true identify:{ name: null // repo name, if null, the local app caption will be used adminEmail: "adminpleasechange@nodomain.com" diff --git a/config/defaults/src/main/resources/metadatasets/xml/mds.xml b/config/defaults/src/main/resources/metadatasets/xml/mds.xml index e3b9eb6169..f4a9fbedea 100644 --- a/config/defaults/src/main/resources/metadatasets/xml/mds.xml +++ b/config/defaults/src/main/resources/metadatasets/xml/mds.xml @@ -1178,27 +1178,6 @@ AND - - ASPECT:"ccm:collection" AND @ccm\:collectionlevel0:true AND OWNER:"${authority}" - - - ASPECT:"ccm:collection" AND @ccm\:collectionlevel0:true AND NOT OWNER:"${authority}" AND NOT - @ccm\:collectiontype:"EDITORIAL" AND NOT @ccm\:collectiontype:"MEDIA_CENTER" - - - - ASPECT:"ccm:collection" AND @ccm\:collectionlevel0:true AND @ccm\:collectiontype:"EDITORIAL" - - - - ASPECT:"ccm:collection" AND @ccm\:collectionlevel0:true AND @ccm\:collectiontype:"MEDIA_CENTER" - - - - ASPECT:"ccm:collection" AND @ccm\:collectionlevel0:true AND NOT @ccm\:collectiontype:"EDITORIAL" AND - NOT @ccm\:collectiontype:"MEDIA_CENTER" - - ASPECT:"sys:archived" @@ -1285,6 +1264,75 @@ ] }} + + + {"bool": + {"must":[ + {"match":{"nodeRef.storeRef.protocol":"workspace"}}, + {"match":{"aspects":"ccm:collection"}}, + {"match":{"properties.ccm:collectionlevel0.keyword":"true"}}, + {"match":{"properties.cm:owner.keyword":"${authority}"}} + ]} + } + + + + + {"bool": + {"must":[ + {"match":{"nodeRef.storeRef.protocol":"workspace"}}, + {"match":{"aspects":"ccm:collection"}}, + {"match":{"properties.ccm:collectionlevel0.keyword":"true"}} + ], + "must_not":[ + {"match":{"permissions.Read":"GROUP_EVERYONE"}}, + {"match":{"properties.cm:owner.keyword":"${authority}"}}, + {"match":{"properties.ccm:collectiontype.keyword":"EDITORIAL"}}, + {"match":{"properties.ccm:collectiontype.keyword":"MEDIA_CENTER"}} + ]} + } + + + + + {"bool": + {"must":[ + {"match":{"nodeRef.storeRef.protocol":"workspace"}}, + {"match":{"aspects":"ccm:collection"}}, + {"match":{"properties.ccm:collectionlevel0.keyword":"true"}}, + {"match":{"properties.ccm:collectiontype.keyword":"EDITORIAL"}} + ]} + } + + + + + {"bool": + {"must":[ + {"match":{"nodeRef.storeRef.protocol":"workspace"}}, + {"match":{"aspects":"ccm:collection"}}, + {"match":{"properties.ccm:collectionlevel0.keyword":"true"}}, + {"match":{"properties.ccm:collectiontype.keyword":"MEDIA_CENTER"}} + ]} + } + + + + + {"bool": + {"must":[ + {"match":{"permissions.Read":"GROUP_EVERYONE"}}, + {"match":{"nodeRef.storeRef.protocol":"workspace"}}, + {"match":{"aspects":"ccm:collection"}}, + {"match":{"properties.ccm:collectionlevel0.keyword":"true"}} + ], + "must_not":[ + {"match":{"properties.ccm:collectiontype.keyword":"EDITORIAL"}}, + {"match":{"properties.ccm:collectiontype.keyword":"MEDIA_CENTER"}} + ]} + } + + true diff --git a/config/defaults/src/main/templates/repository-version.json b/config/defaults/src/main/templates/repository-version.json new file mode 100644 index 0000000000..c6e5a7264c --- /dev/null +++ b/config/defaults/src/main/templates/repository-version.json @@ -0,0 +1,33 @@ +{ + "repository": "repository", + "git": { + "branch": "${git.branch}", + "closest": { + "tag": { + "name": "${git.closest.tag.fixed}" + } + }, + "commit": { + "id": "${git.commit.id}", + "timestamp": { + "datetime": "${git.commit.timestamp.datetime}" + } + }, + "dirty": "${git.dirty}" + }, + "maven": { + "project": { + "artifactId": "${project.artifactId}", + "groupId": "${project.groupId}", + "version": "${project.version}" + } + }, + "version": { + "full": "${git.closest.tag.fixed}", + "major": "${parsedVersion.majorVersion}", + "minor": "${parsedVersion.minorVersion}", + "patch": "${parsedVersion.incrementalVersion}", + "qualifier": "${parsedVersion.qualifier}", + "build": "${parsedVersion.buildNumber}" + } +} diff --git a/deploy/docker/build/service/src/main/build/Dockerfile b/deploy/docker/build/service/src/main/build/Dockerfile index 1a8bbe2ce3..b1f8c18f8d 100644 --- a/deploy/docker/build/service/src/main/build/Dockerfile +++ b/deploy/docker/build/service/src/main/build/Dockerfile @@ -13,7 +13,8 @@ RUN set -eux \ && chown -RL worker:worker /tmp \ && rm \ tomcat/webapps/alfresco/WEB-INF/lib/jsr305-* \ - tomcat/webapps/alfresco/WEB-INF/lib/spring-security-* + tomcat/webapps/alfresco/WEB-INF/lib/spring-security-* \ + tomcat/webapps/alfresco/WEB-INF/lib/oauth2-oidc-sdk-* COPY --chown=worker:worker artifacts/edu_sharing-community-repository-backend-tomcat-${org.edu_sharing:edu_sharing-community-repository-backend-tomcat:jar.version}.jar tomcat/lib/ COPY --chown=worker:worker webapp/*.war tomcat/webapps/edu-sharing.war diff --git a/deploy/docker/build/service/src/main/build/assets/entrypoint.sh b/deploy/docker/build/service/src/main/build/assets/entrypoint.sh index 0c79b35ebd..2dd05c037a 100755 --- a/deploy/docker/build/service/src/main/build/assets/entrypoint.sh +++ b/deploy/docker/build/service/src/main/build/assets/entrypoint.sh @@ -37,6 +37,8 @@ my_home_auth_external_login_provider_target_url="${REPOSITORY_SERVICE_HOME_AUTH_ my_home_provider="${REPOSITORY_SERVICE_HOME_PROVIDER:-}" my_home_url_dynamic="${REPOSITORY_SERVICE_HOME_URL_DYNAMIC:-}" my_home_cookie_attr="${REPOSITORY_SERVICE_HOME_COOKIE_ATTRIBUTES:-}" +my_home_custom_html_headers="${REPOSITORY_SERVICE_HOME_CUSTOM_HTML_HEADERS:-}" + my_allow_origin="${REPOSITORY_SERVICE_ALLOW_ORIGIN:-}" if [[ ! -z "$my_allow_origin" ]]; then my_allow_origin=",${my_allow_origin}" @@ -129,38 +131,38 @@ alfExt="tomcat/shared/classes/alfresco/extension" [[ -n "${cache_host}" && -n "${cache_port}" ]] && { - until wait-for-it "${cache_host}:${cache_port}" -t 3; do sleep 1; done + until wait-for-it "${cache_host}:${cache_port}" -t 3; do sleep 1; done - [[ "${cache_cluster}" == "true" ]] && { - until [[ $(redis-cli --cluster info "${cache_host}" "${cache_port}" | grep '[OK]' | cut -d ' ' -f5) -gt 1 ]]; do - echo >&2 "Waiting for ${cache_host} ..." - sleep 3 - done - } + [[ "${cache_cluster}" == "true" ]] && { + until [[ $(redis-cli --cluster info "${cache_host}" "${cache_port}" | grep '[OK]' | cut -d ' ' -f5) -gt 1 ]]; do + echo >&2 "Waiting for ${cache_host} ..." + sleep 3 + done + } } until wait-for-it "${repository_database_host}:${repository_database_port}" -t 3; do sleep 1; done until PGPASSWORD="${repository_database_pass}" \ - psql -h "${repository_database_host}" -p "${repository_database_port}" -U "${repository_database_user}" -d "${repository_database_name}" -c '\q'; do - echo >&2 "Waiting for ${repository_database_host} ..." - sleep 3 + psql -h "${repository_database_host}" -p "${repository_database_port}" -U "${repository_database_user}" -d "${repository_database_name}" -c '\q'; do + echo >&2 "Waiting for ${repository_database_host} ..." + sleep 3 done # jodconverter [[ -n "${repository_transform_host}" && -n "${repository_transform_port}" ]] && { - until wait-for-it "${repository_transform_host}:${repository_transform_port}" -t 3; do sleep 1; done + until wait-for-it "${repository_transform_host}:${repository_transform_port}" -t 3; do sleep 1; done } # core aio transformer [[ -n "${repository_transform_aio_host}" && -n "${repository_transform_aio_port}" ]] && { - until wait-for-it "${repository_transform_aio_host}:${repository_transform_aio_port}" -t 3; do sleep 1; done + until wait-for-it "${repository_transform_aio_host}:${repository_transform_aio_port}" -t 3; do sleep 1; done } # edu-sharing custom transformer [[ -n "${repository_transform_es_host}" && -n "${repository_transform_es_port}" ]] && { - until wait-for-it "${repository_transform_es_host}:${repository_transform_es_port}" -t 3; do sleep 1; done + until wait-for-it "${repository_transform_es_host}:${repository_transform_es_port}" -t 3; do sleep 1; done } ### config ############################################################################################################# @@ -169,24 +171,32 @@ done cp "${eduCConfX}" "${eduCConf}" } -configs=(cluster node) +if [[ -f tomcat/shared/classes/config/defaults/project-version.json ]]; then + versionFile="tomcat/shared/classes/config/defaults/project-version.json" +else + versionFile="tomcat/shared/classes/config/defaults/repository-version.json" +fi + +configs=(cluster node) for config in "${configs[@]}"; do - if [[ ! -f tomcat/shared/classes/config/$config/version.json ]]; then - mkdir -p tomcat/shared/classes/config/$config - for jar in tomcat/shared/assets/$config/*.jar; do - if [[ -f $jar ]] ; then + if [[ ! -f tomcat/shared/classes/config/$config/version.json ]]; then + mkdir -p tomcat/shared/classes/config/$config + for jar in tomcat/shared/assets/$config/*.jar; do + if [[ -f $jar ]]; then unzip -o $jar -d tomcat/shared/classes/config/$config -x 'META-INF/*' - fi - done - cp tomcat/webapps/edu-sharing/WEB-INF/classes/version.json tomcat/shared/classes/config/$config/version.json - cp tomcat/shared/classes/config/$config/version.json tomcat/shared/classes/config/$config/version.json.$(date +%d-%m-%Y_%H-%M-%S ) - else - cmp -s tomcat/webapps/edu-sharing/WEB-INF/classes/version.json tomcat/shared/classes/config/$config/version.json || { - cp tomcat/webapps/edu-sharing/WEB-INF/classes/version.json tomcat/shared/classes/config/$config/version.json - cp tomcat/shared/classes/config/$config/version.json tomcat/shared/classes/config/$config/version.json.$(date +%d-%m-%Y_%H-%M-%S ) - } - fi + fi + done + + cp "${versionFile}" tomcat/shared/classes/config/$config/version.json + cp "${versionFile}" tomcat/shared/classes/config/$config/version.json.$(date +%d-%m-%Y_%H-%M-%S) + + else + cmp -s "${versionFile}" tomcat/shared/classes/config/$config/version.json || { + cp "${versionFile}" tomcat/shared/classes/config/$config/version.json + cp "${versionFile}" tomcat/shared/classes/config/$config/version.json.$(date +%d-%m-%Y_%H-%M-%S) + } + fi done reinstall.sh @@ -207,14 +217,13 @@ export CATALINA_OPTS="-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xe export CATALINA_OPTS="-Dhazelcast.ignoreXxeProtectionFailures=true $CATALINA_OPTS" export CATALINA_OPTS="--add-modules java.se --add-exports java.base/jdk.internal.ref=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.management/sun.management=ALL-UNNAMED --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED $CATALINA_OPTS" - xmlstarlet ed -L \ - -d '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]/@hostConfigClass' \ - -i '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]' -t attr -n 'hostConfigClass' -v 'org.edu_sharing.catalina.startup.OrderedHostConfig' \ - ${catSConf} + -d '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]/@hostConfigClass' \ + -i '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]' -t attr -n 'hostConfigClass' -v 'org.edu_sharing.catalina.startup.OrderedHostConfig' \ + ${catSConf} xmlstarlet ed -L \ - -d '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]/Valve[@className="org.apache.catalina.valves.ErrorReportValve"]' \ + -d '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]/Valve[@className="org.apache.catalina.valves.ErrorReportValve"]' \ -s '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]' -t elem -n 'Valve' -v '' \ --var valve '$prev' \ -i '$valve' -t attr -n "className" -v "org.apache.catalina.valves.ErrorReportValve" \ @@ -223,84 +232,84 @@ xmlstarlet ed -L \ ${catSConf} xmlstarlet ed -L \ - -d '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]/Valve[@className="org.apache.catalina.valves.AccessLogValve"]' \ - ${catSConf} + -d '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]/Valve[@className="org.apache.catalina.valves.AccessLogValve"]' \ + ${catSConf} [[ -n $my_http_jvmroute ]] && { - xmlstarlet ed -L \ - -i '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]]' -t attr -n 'jvmRoute' -v "${my_http_jvmroute}" \ - ${catSConf} + xmlstarlet ed -L \ + -i '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]]' -t attr -n 'jvmRoute' -v "${my_http_jvmroute}" \ + ${catSConf} } [[ -n $my_http_accesslog_enabled ]] && { - xmlstarlet ed -L \ - -s '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]' -t elem -n 'Valve' -v '' \ - --var valve '$prev' \ - -i '$valve' -t attr -n "className" -v "org.edu_sharing.catalina.valves.StdoutAccessLogValve" \ - -i '$valve' -t attr -n "pattern" -v "%h %l %u %t "%r" %s %b" \ - ${catSConf} + xmlstarlet ed -L \ + -s '/Server/Service[@name="Catalina"]/Engine[@name="Catalina"]/Host[@name="localhost"]' -t elem -n 'Valve' -v '' \ + --var valve '$prev' \ + -i '$valve' -t attr -n "className" -v "org.edu_sharing.catalina.valves.StdoutAccessLogValve" \ + -i '$valve' -t attr -n "pattern" -v "%h %l %u %t "%r" %s %b" \ + ${catSConf} } xmlstarlet ed -L \ - -d '/Server/Service[@name="Catalina"]/Connector' \ - -s '/Server/Service[@name="Catalina"]' -t elem -n 'Connector' -v '' \ - --var internal '$prev' \ - -i '$internal' -t attr -n "address" -v "${my_bind}" \ - -i '$internal' -t attr -n "port" -v "8080" \ - -i '$internal' -t attr -n "scheme" -v "http" \ - -i '$internal' -t attr -n "protocol" -v "org.apache.coyote.http11.Http11NioProtocol" \ - -i '$internal' -t attr -n "connectionTimeout" -v "${my_wait_internal}" \ - -i '$internal' -t attr -n "maxThreads" -v "${my_pool_internal}" \ - -s '/Server/Service[@name="Catalina"]' -t elem -n 'Connector' -v '' \ - --var external1 '$prev' \ - -i '$external1' -t attr -n "address" -v "${my_bind}" \ - -i '$external1' -t attr -n "port" -v "8081" \ - -i '$external1' -t attr -n "scheme" -v "${my_prot_external}" \ - -i '$external1' -t attr -n "protocol" -v "org.apache.coyote.http11.Http11NioProtocol" \ - -i '$external1' -t attr -n "connectionTimeout" -v "${my_wait_external}" \ - -i '$external1' -t attr -n "maxThreads" -v "${my_pool_external}" \ - -s '/Server/Service[@name="Catalina"]' -t elem -n 'Connector' -v '' \ - --var external2 '$prev' \ - -i '$external2' -t attr -n "address" -v "${my_bind}" \ - -i '$external2' -t attr -n "port" -v "8009" \ - -i '$external2' -t attr -n "scheme" -v "${my_prot_external}" \ - -i '$external2' -t attr -n "protocol" -v "org.apache.coyote.ajp.AjpNioProtocol" \ - -i '$external2' -t attr -n "URIEncoding" -v "UTF-8" \ - -i '$external2' -t attr -n "connectionTimeout" -v "${my_wait_external}" \ - -i '$external2' -t attr -n "maxThreads" -v "${my_pool_external}" \ - -i '$external2' -t attr -n "secretRequired" -v "false" \ - -i '$external2' -t attr -n "tomcatAuthentication" -v "false" \ - -i '$external2' -t attr -n "allowedRequestAttributesPattern" -v ".*" \ - -i '$external2' -t attr -n "packetSize" -v "${my_proxy_buffer_size}" \ - ${catSConf} + -d '/Server/Service[@name="Catalina"]/Connector' \ + -s '/Server/Service[@name="Catalina"]' -t elem -n 'Connector' -v '' \ + --var internal '$prev' \ + -i '$internal' -t attr -n "address" -v "${my_bind}" \ + -i '$internal' -t attr -n "port" -v "8080" \ + -i '$internal' -t attr -n "scheme" -v "http" \ + -i '$internal' -t attr -n "protocol" -v "org.apache.coyote.http11.Http11NioProtocol" \ + -i '$internal' -t attr -n "connectionTimeout" -v "${my_wait_internal}" \ + -i '$internal' -t attr -n "maxThreads" -v "${my_pool_internal}" \ + -s '/Server/Service[@name="Catalina"]' -t elem -n 'Connector' -v '' \ + --var external1 '$prev' \ + -i '$external1' -t attr -n "address" -v "${my_bind}" \ + -i '$external1' -t attr -n "port" -v "8081" \ + -i '$external1' -t attr -n "scheme" -v "${my_prot_external}" \ + -i '$external1' -t attr -n "protocol" -v "org.apache.coyote.http11.Http11NioProtocol" \ + -i '$external1' -t attr -n "connectionTimeout" -v "${my_wait_external}" \ + -i '$external1' -t attr -n "maxThreads" -v "${my_pool_external}" \ + -s '/Server/Service[@name="Catalina"]' -t elem -n 'Connector' -v '' \ + --var external2 '$prev' \ + -i '$external2' -t attr -n "address" -v "${my_bind}" \ + -i '$external2' -t attr -n "port" -v "8009" \ + -i '$external2' -t attr -n "scheme" -v "${my_prot_external}" \ + -i '$external2' -t attr -n "protocol" -v "org.apache.coyote.ajp.AjpNioProtocol" \ + -i '$external2' -t attr -n "URIEncoding" -v "UTF-8" \ + -i '$external2' -t attr -n "connectionTimeout" -v "${my_wait_external}" \ + -i '$external2' -t attr -n "maxThreads" -v "${my_pool_external}" \ + -i '$external2' -t attr -n "secretRequired" -v "false" \ + -i '$external2' -t attr -n "tomcatAuthentication" -v "false" \ + -i '$external2' -t attr -n "allowedRequestAttributesPattern" -v ".*" \ + -i '$external2' -t attr -n "packetSize" -v "${my_proxy_buffer_size}" \ + ${catSConf} [[ -n "${cache_host}" && -n "${cache_port}" ]] && { - xmlstarlet ed -L \ - -d '/Context/Manager[@className="org.redisson.tomcat.RedissonSessionManager"]' \ - -s '/Context' -t elem -n "Manager" -v "" \ - --var redis '$prev' \ - -i '$redis' -t attr -n "className" -v "org.redisson.tomcat.RedissonSessionManager" \ - -i '$redis' -t attr -n "configPath" -v "tomcat/conf/redisson.yaml" \ - -i '$redis' -t attr -n "readMode" -v "REDIS" \ - -i '$redis' -t attr -n "updateMode" -v "DEFAULT" \ - -i '$redis' -t attr -n "broadcastSessionEvents" -v "false" \ - -i '$redis' -t attr -n "broadcastSessionUpdates" -v "false" \ - ${catCConf} - - if [[ "${cache_cluster}" == "true" ]] ; then - { - echo "clusterServersConfig:" - echo " nodeAddresses:" - echo " - \"redis://${cache_host}:${cache_port}\"" - } >> tomcat/conf/redisson.yaml - else - { - echo "singleServerConfig:" - echo " address: \"redis://${CACHE_HOST}:${CACHE_PORT}\"" - echo " database: ${cache_database}" - } >> tomcat/conf/redisson.yaml - fi - echo "codec: ! {}" >> tomcat/conf/redisson.yaml + xmlstarlet ed -L \ + -d '/Context/Manager[@className="org.redisson.tomcat.RedissonSessionManager"]' \ + -s '/Context' -t elem -n "Manager" -v "" \ + --var redis '$prev' \ + -i '$redis' -t attr -n "className" -v "org.redisson.tomcat.RedissonSessionManager" \ + -i '$redis' -t attr -n "configPath" -v "tomcat/conf/redisson.yaml" \ + -i '$redis' -t attr -n "readMode" -v "REDIS" \ + -i '$redis' -t attr -n "updateMode" -v "DEFAULT" \ + -i '$redis' -t attr -n "broadcastSessionEvents" -v "false" \ + -i '$redis' -t attr -n "broadcastSessionUpdates" -v "false" \ + ${catCConf} + + if [[ "${cache_cluster}" == "true" ]]; then + { + echo "clusterServersConfig:" + echo " nodeAddresses:" + echo " - \"redis://${cache_host}:${cache_port}\"" + } >>tomcat/conf/redisson.yaml + else + { + echo "singleServerConfig:" + echo " address: \"redis://${CACHE_HOST}:${CACHE_PORT}\"" + echo " database: ${cache_database}" + } >>tomcat/conf/redisson.yaml + fi + echo "codec: ! {}" >>tomcat/conf/redisson.yaml } ### Alfresco platform ################################################################################################## @@ -414,11 +423,11 @@ sed -i -r 's|^[#]*\s*localTransform\.core-aio\.url=.*|localTransform.core-aio.ur grep -q '^[#]*\s*localTransform\.core-aio\.url=' "${alfProps}" || echo "localTransform.core-aio.url=http://${repository_transform_aio_host}:${repository_transform_aio_port}/" >>"${alfProps}" xmlstarlet ed -L \ - -s '_:web-app/_:filter[_:filter-name="X509AuthFilter"]' -t elem -n "init-param" -v '' \ - --var param '$prev' \ - -s '$param' -t elem -n "param-name" -v "allow-unauthenticated-solr-endpoint" \ - -s '$param' -t elem -n "param-value" -v "true" \ - ${catAlfWConf} + -s '_:web-app/_:filter[_:filter-name="X509AuthFilter"]' -t elem -n "init-param" -v '' \ + --var param '$prev' \ + -s '$param' -t elem -n "param-name" -v "allow-unauthenticated-solr-endpoint" \ + -s '$param' -t elem -n "param-value" -v "true" \ + ${catAlfWConf} [[ -f ${catAlfLog} ]] && { @@ -430,31 +439,43 @@ xmlstarlet ed -L \ my_origin="${my_prot_external}://${my_host_external}" [[ $my_port_external != "80" && $my_port_external != "443" ]] && { - my_origin="${my_origin}:${my_port_external}" + my_origin="${my_origin}:${my_port_external}" } xmlstarlet ed -L \ - -u '/properties/entry[@key="appid"]' -v "${my_home_appid}" \ - -u '/properties/entry[@key="authenticationwebservice"]' -v "${my_auth_external}" \ - -u '/properties/entry[@key="clientport"]' -v "${my_port_external}" \ - -u '/properties/entry[@key="clientprotocol"]' -v "${my_prot_external}" \ - -u '/properties/entry[@key="domain"]' -v "${my_host_external}" \ - -u '/properties/entry[@key="host"]' -v "${my_host_internal}" \ - -u '/properties/entry[@key="password"]' -v "${my_admin_pass}" \ - -u '/properties/entry[@key="port"]' -v "${my_port_internal}" \ - -u '/properties/entry[@key="allow_origin"]' -v "${my_origin},http://localhost:54361${my_allow_origin}" \ - ${homeProp} + -u '/properties/entry[@key="appid"]' -v "${my_home_appid}" \ + -u '/properties/entry[@key="authenticationwebservice"]' -v "${my_auth_external}" \ + -u '/properties/entry[@key="clientport"]' -v "${my_port_external}" \ + -u '/properties/entry[@key="clientprotocol"]' -v "${my_prot_external}" \ + -u '/properties/entry[@key="domain"]' -v "${my_host_external}" \ + -u '/properties/entry[@key="host"]' -v "${my_host_internal}" \ + -u '/properties/entry[@key="password"]' -v "${my_admin_pass}" \ + -u '/properties/entry[@key="port"]' -v "${my_port_internal}" \ + -u '/properties/entry[@key="allow_origin"]' -v "${my_origin},http://localhost:54361${my_allow_origin}" \ + ${homeProp} xmlstarlet ed -L \ -d '/properties/entry[@key="cookie_attributes"]' \ ${homeProp} [[ -n "${my_home_cookie_attr}" ]] && { - xmlstarlet ed -L \ - -s '/properties' -t elem -n "entry" -v "${my_home_cookie_attr}" \ - --var entry '$prev' \ - -i '$entry' -t attr -n "key" -v "cookie_attributes" \ - ${homeProp} + xmlstarlet ed -L \ + -s '/properties' -t elem -n "entry" -v "${my_home_cookie_attr}" \ + --var entry '$prev' \ + -i '$entry' -t attr -n "key" -v "cookie_attributes" \ + ${homeProp} +} + +xmlstarlet ed -L \ + -d '/properties/entry[@key="custom_html_headers"]' \ + ${homeProp} + +[[ -n "${my_home_custom_html_headers}" ]] && { + xmlstarlet ed -L \ + -s '/properties' -t elem -n "entry" -v "${my_home_custom_html_headers}" \ + --var entry '$prev' \ + -i '$entry' -t attr -n "key" -v "custom_html_headers" \ + ${homeProp} } xmlstarlet ed -L \ @@ -462,11 +483,11 @@ xmlstarlet ed -L \ ${homeProp} [[ -n "${my_guest_user}" ]] && { - xmlstarlet ed -L \ - -s '/properties' -t elem -n "entry" -v "${my_guest_user}" \ - --var entry '$prev' \ - -i '$entry' -t attr -n "key" -v "guest_username" \ - ${homeProp} + xmlstarlet ed -L \ + -s '/properties' -t elem -n "entry" -v "${my_guest_user}" \ + --var entry '$prev' \ + -i '$entry' -t attr -n "key" -v "guest_username" \ + ${homeProp} } xmlstarlet ed -L \ @@ -474,72 +495,63 @@ xmlstarlet ed -L \ ${homeProp} [[ -n "${my_guest_pass}" ]] && { - xmlstarlet ed -L \ - -s '/properties' -t elem -n "entry" -v "${my_guest_pass}" \ - --var entry '$prev' \ - -i '$entry' -t attr -n "key" -v "guest_password" \ - ${homeProp} + xmlstarlet ed -L \ + -s '/properties' -t elem -n "entry" -v "${my_guest_pass}" \ + --var entry '$prev' \ + -i '$entry' -t attr -n "key" -v "guest_password" \ + ${homeProp} } -xmlstarlet ed -L \ - -d '/properties/entry[@key="allowed_authentication_types"]' \ - ${homeProp} - [[ -n "${my_home_auth}" ]] && { - xmlstarlet ed -L \ - -s '/properties' -t elem -n "entry" -v "${my_home_auth}" \ - --var entry '$prev' \ - -i '$entry' -t attr -n "key" -v "allowed_authentication_types" \ - ${homeProp} - if [[ "${my_home_auth_external}" == "true" ]] ; then - xmlstarlet ed -L \ - -s '/config/values' -t elem -n 'loginUrl' -v '' \ - -d '/config/values/loginUrl[position() != 1]' \ - -u '/config/values/loginUrl' -v "${my_home_auth_external_login}" \ - -s '/config/values' -t elem -n 'loginProvidersUrl' -v '' \ - -d '/config/values/loginProvidersUrl[position() != 1]' \ - -u '/config/values/loginProvidersUrl' -v "${my_home_auth_external_login_providers_url}" \ - -s '/config/values' -t elem -n 'loginProviderTargetUrl' -v '' \ - -d '/config/values/loginProviderTargetUrl[position() != 1]' \ - -u '/config/values/loginProviderTargetUrl' -v "${my_home_auth_external_login_provider_target_url}" \ - -s '/config/values' -t elem -n 'logout' -v '' \ - -d '/config/values/logout[position() != 1]' \ - -s '/config/values/logout' -t elem -n 'url' -v '' \ - -d '/config/values/logout/url[position() != 1]' \ - -u '/config/values/logout/url' -v "${my_home_auth_external_logout}" \ - -s '/config/values/logout' -t elem -n 'destroySession' -v '' \ - -d '/config/values/logout/destroySession[position() != 1]' \ - -u '/config/values/logout/destroySession' -v "${my_home_auth_external_logout_destroy_session}" \ - ${eduCConf} - - if [[ "${my_home_auth_external_logout_redirect}" == "true" ]] ; then - xmlstarlet ed -L \ - -s '/config/values/logout' -t elem -n 'ajax' -v '' \ - -d '/config/values/logout/ajax[position() != 1]' \ - -u '/config/values/logout/ajax' -v 'true' \ - -s '/config/values/logout' -t elem -n 'next' -v '' \ - -d '/config/values/logout/next[position() != 1]' \ - -u '/config/values/logout/next' -v "${my_home_auth_external_logout_redirect_url}" \ - ${eduCConf} - fi + if [[ "${my_home_auth_external}" == "true" ]]; then + xmlstarlet ed -L \ + -s '/config/values' -t elem -n 'loginUrl' -v '' \ + -d '/config/values/loginUrl[position() != 1]' \ + -u '/config/values/loginUrl' -v "${my_home_auth_external_login}" \ + -s '/config/values' -t elem -n 'loginProvidersUrl' -v '' \ + -d '/config/values/loginProvidersUrl[position() != 1]' \ + -u '/config/values/loginProvidersUrl' -v "${my_home_auth_external_login_providers_url}" \ + -s '/config/values' -t elem -n 'loginProviderTargetUrl' -v '' \ + -d '/config/values/loginProviderTargetUrl[position() != 1]' \ + -u '/config/values/loginProviderTargetUrl' -v "${my_home_auth_external_login_provider_target_url}" \ + -s '/config/values' -t elem -n 'logout' -v '' \ + -d '/config/values/logout[position() != 1]' \ + -s '/config/values/logout' -t elem -n 'url' -v '' \ + -d '/config/values/logout/url[position() != 1]' \ + -u '/config/values/logout/url' -v "${my_home_auth_external_logout}" \ + -s '/config/values/logout' -t elem -n 'destroySession' -v '' \ + -d '/config/values/logout/destroySession[position() != 1]' \ + -u '/config/values/logout/destroySession' -v "${my_home_auth_external_logout_destroy_session}" \ + ${eduCConf} + + if [[ "${my_home_auth_external_logout_redirect}" == "true" ]]; then + xmlstarlet ed -L \ + -s '/config/values/logout' -t elem -n 'ajax' -v '' \ + -d '/config/values/logout/ajax[position() != 1]' \ + -u '/config/values/logout/ajax' -v 'true' \ + -s '/config/values/logout' -t elem -n 'next' -v '' \ + -d '/config/values/logout/next[position() != 1]' \ + -u '/config/values/logout/next' -v "${my_home_auth_external_logout_redirect_url}" \ + ${eduCConf} + fi else - sed -i -r 's|||g' tomcat/webapps/edu-sharing/WEB-INF/web.xml - xmlstarlet ed -L \ - -s '/config/values' -t elem -n 'loginUrl' -v '' \ - -d '/config/values/loginUrl[position() != 1]' \ - -u '/config/values/loginUrl' -v "${my_path_external}/shibboleth" \ - -s '/config/values' -t elem -n 'logout' -v '' \ - -d '/config/values/logout[position() != 1]' \ - -s '/config/values/logout' -t elem -n 'url' -v '' \ - -d '/config/values/logout/url[position() != 1]' \ - -u '/config/values/logout/url' -v "${my_path_external}/logout" \ - -s '/config/values/logout' -t elem -n 'destroySession' -v '' \ - -d '/config/values/logout/destroySession[position() != 1]' \ - -u '/config/values/logout/destroySession' -v 'false' \ - ${eduCConf} - fi + sed -i -r 's|||g' tomcat/webapps/edu-sharing/WEB-INF/web.xml + xmlstarlet ed -L \ + -s '/config/values' -t elem -n 'loginUrl' -v '' \ + -d '/config/values/loginUrl[position() != 1]' \ + -u '/config/values/loginUrl' -v "${my_path_external}/shibboleth" \ + -s '/config/values' -t elem -n 'logout' -v '' \ + -d '/config/values/logout[position() != 1]' \ + -s '/config/values/logout' -t elem -n 'url' -v '' \ + -d '/config/values/logout/url[position() != 1]' \ + -u '/config/values/logout/url' -v "${my_path_external}/logout" \ + -s '/config/values/logout' -t elem -n 'destroySession' -v '' \ + -d '/config/values/logout/destroySession[position() != 1]' \ + -u '/config/values/logout/destroySession' -v 'false' \ + ${eduCConf} + fi } xmlstarlet ed -L \ @@ -547,11 +559,11 @@ xmlstarlet ed -L \ ${homeProp} [[ -n "${my_home_provider}" ]] && { - xmlstarlet ed -L \ - -s '/properties' -t elem -n "entry" -v "${my_home_provider}" \ - --var entry '$prev' \ - -i '$entry' -t attr -n "key" -v "remote_provider" \ - ${homeProp} + xmlstarlet ed -L \ + -s '/properties' -t elem -n "entry" -v "${my_home_provider}" \ + --var entry '$prev' \ + -i '$entry' -t attr -n "key" -v "remote_provider" \ + ${homeProp} } [[ -n "${my_home_url_dynamic}" ]] && { @@ -567,207 +579,207 @@ xmlstarlet ed -L \ hocon -f ${eduSConf} unset "repository.mail.from" } [[ -n "${my_mail_from}" ]] && { - hocon -f ${eduSConf} set "repository.mail.from" '"'"${my_mail_from}"'"' + hocon -f ${eduSConf} set "repository.mail.from" '"'"${my_mail_from}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.addReplyTo" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.addReplyTo" } [[ -n "${my_mail_addreplyto}" ]] && { - hocon -f ${eduSConf} set "repository.mail.addReplyTo" '"'"${my_mail_addreplyto}"'"' + hocon -f ${eduSConf} set "repository.mail.addReplyTo" '"'"${my_mail_addreplyto}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.register.receiver" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.register.receiver" } [[ -n "${my_mail_register_receiver}" ]] && { - hocon -f ${eduSConf} set "repository.mail.register.receiver" '"'"${my_mail_register_receiver}"'"' + hocon -f ${eduSConf} set "repository.mail.register.receiver" '"'"${my_mail_register_receiver}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.report.receivers" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.report.receivers" } [[ -n "${my_mail_report_receiver}" ]] && { - hocon -f ${eduSConf} set "repository.mail.report.receivers" "[\"${my_mail_report_receiver//,/\",\"}\"]" + hocon -f ${eduSConf} set "repository.mail.report.receivers" "[\"${my_mail_report_receiver//,/\",\"}\"]" } [[ $(hocon -f ${eduSConf} get "repository.mail.server.smtp.host" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.server.smtp.host" } [[ -n "${my_mail_server_smtp_host}" ]] && { - hocon -f ${eduSConf} set "repository.mail.server.smtp.host" '"'"${my_mail_server_smtp_host}"'"' + hocon -f ${eduSConf} set "repository.mail.server.smtp.host" '"'"${my_mail_server_smtp_host}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.server.smtp.port" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.server.smtp.port" } [[ -n "${my_mail_server_smtp_port}" ]] && { - hocon -f ${eduSConf} set "repository.mail.server.smtp.port" '"'"${my_mail_server_smtp_port}"'"' + hocon -f ${eduSConf} set "repository.mail.server.smtp.port" '"'"${my_mail_server_smtp_port}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.server.smtp.username" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.server.smtp.username" } [[ -n "${my_mail_server_smtp_username}" ]] && { - hocon -f ${eduSConf} set "repository.mail.server.smtp.username" '"'"${my_mail_server_smtp_username}"'"' + hocon -f ${eduSConf} set "repository.mail.server.smtp.username" '"'"${my_mail_server_smtp_username}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.server.smtp.password" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.server.smtp.password" } [[ -n "${my_mail_server_smtp_password}" ]] && { - hocon -f ${eduSConf} set "repository.mail.server.smtp.password" '"'"${my_mail_server_smtp_password}"'"' + hocon -f ${eduSConf} set "repository.mail.server.smtp.password" '"'"${my_mail_server_smtp_password}"'"' } [[ $(hocon -f ${eduSConf} get "repository.mail.server.smtp.authtype" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.mail.server.smtp.authtype" } [[ -n "${my_mail_server_smtp_authtype}" ]] && { - hocon -f ${eduSConf} set "repository.mail.server.smtp.authtype" '"'"${my_mail_server_smtp_authtype}"'"' + hocon -f ${eduSConf} set "repository.mail.server.smtp.authtype" '"'"${my_mail_server_smtp_authtype}"'"' } [[ $(hocon -f ${eduSConf} get "repository.httpclient.disableSNI4Hosts" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.disableSNI4Hosts" } [[ -n "${my_http_client_disablesni4hosts}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.disableSNI4Hosts" '"'"${my_http_client_disablesni4hosts}"'"' + hocon -f ${eduSConf} set "repository.httpclient.disableSNI4Hosts" '"'"${my_http_client_disablesni4hosts}"'"' } [[ $(hocon -f ${eduSConf} get "repository.httpclient.proxy.host" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.proxy.host" } [[ -n "${my_http_client_proxy_host}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.proxy.host" '"'"${my_http_client_proxy_host}"'"' + hocon -f ${eduSConf} set "repository.httpclient.proxy.host" '"'"${my_http_client_proxy_host}"'"' } [[ -n "${my_http_client_proxy_nonproxyhosts}" ]] && { - export CATALINA_OPTS="-Dhttp.nonProxyHosts=\"${my_http_client_proxy_nonproxyhosts//,/|}\" $CATALINA_OPTS" - export CATALINA_OPTS="-Dhttps.nonProxyHosts=\"${my_http_client_proxy_nonproxyhosts//,/|}\" $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttp.nonProxyHosts=\"${my_http_client_proxy_nonproxyhosts//,/|}\" $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttps.nonProxyHosts=\"${my_http_client_proxy_nonproxyhosts//,/|}\" $CATALINA_OPTS" } [[ $(hocon -f ${eduSConf} get "repository.httpclient.proxy.nonproxyhosts" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.proxy.nonproxyhosts" } [[ -n "${my_http_client_proxy_nonproxyhosts}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.proxy.nonproxyhosts" '"'"${my_http_client_proxy_nonproxyhosts}"'"' + hocon -f ${eduSConf} set "repository.httpclient.proxy.nonproxyhosts" '"'"${my_http_client_proxy_nonproxyhosts}"'"' } [[ -n "${my_http_client_proxy_proxyhost}" ]] && { - export CATALINA_OPTS="-Dhttp.proxyHost=${my_http_client_proxy_proxyhost} $CATALINA_OPTS" - export CATALINA_OPTS="-Dhttps.proxyHost=${my_http_client_proxy_proxyhost} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttp.proxyHost=${my_http_client_proxy_proxyhost} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttps.proxyHost=${my_http_client_proxy_proxyhost} $CATALINA_OPTS" } [[ $(hocon -f ${eduSConf} get "repository.httpclient.proxy.proxyhost" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.proxy.proxyhost" } [[ -n "${my_http_client_proxy_proxyhost}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.proxy.proxyhost" '"'"${my_http_client_proxy_proxyhost}"'"' + hocon -f ${eduSConf} set "repository.httpclient.proxy.proxyhost" '"'"${my_http_client_proxy_proxyhost}"'"' } [[ -n "${my_http_client_proxy_proxypass}" ]] && { - export CATALINA_OPTS="-Dhttp.proxyPass=${my_http_client_proxy_proxypass} $CATALINA_OPTS" - export CATALINA_OPTS="-Dhttps.proxyPass=${my_http_client_proxy_proxypass} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttp.proxyPass=${my_http_client_proxy_proxypass} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttps.proxyPass=${my_http_client_proxy_proxypass} $CATALINA_OPTS" } [[ $(hocon -f ${eduSConf} get "repository.httpclient.proxy.proxypass" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.proxy.proxypass" } [[ -n "${my_http_client_proxy_proxypass}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.proxy.proxypass" '"'"${my_http_client_proxy_proxypass}"'"' + hocon -f ${eduSConf} set "repository.httpclient.proxy.proxypass" '"'"${my_http_client_proxy_proxypass}"'"' } [[ -n "${my_http_client_proxy_proxyport}" ]] && { - export CATALINA_OPTS="-Dhttp.proxyPort=${my_http_client_proxy_proxyport} $CATALINA_OPTS" - export CATALINA_OPTS="-Dhttps.proxyPort=${my_http_client_proxy_proxyport} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttp.proxyPort=${my_http_client_proxy_proxyport} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttps.proxyPort=${my_http_client_proxy_proxyport} $CATALINA_OPTS" } [[ $(hocon -f ${eduSConf} get "repository.httpclient.proxy.proxyport" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.proxy.proxyport" } [[ -n "${my_http_client_proxy_proxyport}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.proxy.proxyport" '"'"${my_http_client_proxy_proxyport}"'"' + hocon -f ${eduSConf} set "repository.httpclient.proxy.proxyport" '"'"${my_http_client_proxy_proxyport}"'"' } [[ -n "${my_http_client_proxy_proxyuser}" ]] && { - export CATALINA_OPTS="-Dhttp.proxyUser=${my_http_client_proxy_proxyuser} $CATALINA_OPTS" - export CATALINA_OPTS="-Dhttps.proxyUser=${my_http_client_proxy_proxyuser} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttp.proxyUser=${my_http_client_proxy_proxyuser} $CATALINA_OPTS" + export CATALINA_OPTS="-Dhttps.proxyUser=${my_http_client_proxy_proxyuser} $CATALINA_OPTS" } [[ $(hocon -f ${eduSConf} get "repository.httpclient.proxy.proxyuser" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "repository.httpclient.proxy.proxyuser" } [[ -n "${my_http_client_proxy_proxyuser}" ]] && { - hocon -f ${eduSConf} set "repository.httpclient.proxy.proxyuser" '"'"${my_http_client_proxy_proxyuser}"'"' + hocon -f ${eduSConf} set "repository.httpclient.proxy.proxyuser" '"'"${my_http_client_proxy_proxyuser}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.default-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.default-src" } [[ -n "${my_http_server_csp_default}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.default-src" '"'"${my_http_server_csp_default}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.default-src" '"'"${my_http_server_csp_default}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.connect-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.connect-src" } [[ -n "${my_http_server_csp_connect}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.connect-src" '"'"${my_http_server_csp_connect}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.connect-src" '"'"${my_http_server_csp_connect}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.img-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.img-src" } [[ -n "${my_http_server_csp_img}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.img-src" '"'"${my_http_server_csp_img}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.img-src" '"'"${my_http_server_csp_img}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.script-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.script-src" } [[ -n "${my_http_server_csp_script}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.script-src" '"'"${my_http_server_csp_script}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.script-src" '"'"${my_http_server_csp_script}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.font-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.font-src" } [[ -n "${my_http_server_csp_font}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.font-src" '"'"${my_http_server_csp_font}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.font-src" '"'"${my_http_server_csp_font}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.frame-ancestors" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.frame-ancestors" } [[ -n "${my_http_server_csp_frame}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.frame-ancestors" '"'"${my_http_server_csp_frame}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.frame-ancestors" '"'"${my_http_server_csp_frame}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.media-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.media-src" } [[ -n "${my_http_server_csp_media}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.media-src" '"'"${my_http_server_csp_media}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.media-src" '"'"${my_http_server_csp_media}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.object-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.object-src" } [[ -n "${my_http_server_csp_object}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.object-src" '"'"${my_http_server_csp_object}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.object-src" '"'"${my_http_server_csp_object}"'"' } [[ $(hocon -f ${eduSConf} get "angular.headers.Content-Security-Policy.style-src" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "angular.headers.Content-Security-Policy.style-src" } [[ -n "${my_http_server_csp_style}" ]] && { - hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.style-src" '"'"${my_http_server_csp_style}"'"' + hocon -f ${eduSConf} set "angular.headers.Content-Security-Policy.style-src" '"'"${my_http_server_csp_style}"'"' } [[ $(hocon -f ${eduSConf} get "jobs.primaryHostname" 2>/dev/null) ]] && { hocon -f ${eduSConf} unset "jobs.primaryHostname" } [[ -n "${my_jobs_primary_hostname}" ]] && { - hocon -f ${eduSConf} set "jobs.primaryHostname" '"'"${my_jobs_primary_hostname}"'"' + hocon -f ${eduSConf} set "jobs.primaryHostname" '"'"${my_jobs_primary_hostname}"'"' } # clean up empty lines in config after hocon commands @@ -775,16 +787,16 @@ sed -i '/^[[:space:]]*$/d' ${eduSConf} xmlstarlet ed -L \ -N x="http://java.sun.com/xml/ns/javaee" \ - -u '/x:web-app/x:session-config/x:session-timeout' -v "${my_http_server_session_timeout}" \ - ${catEduWConf} + -u '/x:web-app/x:session-config/x:session-timeout' -v "${my_http_server_session_timeout}" \ + ${catEduWConf} ######################################################################################################################## # PLUGIN for entrypoint in bin/plugins/plugin-*/entrypoint.sh; do - [[ -f $entrypoint ]] && { - source "$entrypoint" || exit 1 - } + [[ -f $entrypoint ]] && { + source "$entrypoint" || exit 1 + } done ######################################################################################################################## diff --git a/deploy/docker/compose/src/main/compose/1_repository-common.yml b/deploy/docker/compose/src/main/compose/1_repository-common.yml index 3e3cec8858..492f1aaed6 100644 --- a/deploy/docker/compose/src/main/compose/1_repository-common.yml +++ b/deploy/docker/compose/src/main/compose/1_repository-common.yml @@ -103,6 +103,7 @@ services: REPOSITORY_SERVICE_GUEST_PASS: "${REPOSITORY_SERVICE_GUEST_PASS:-}" REPOSITORY_SERVICE_GUEST_USER: "${REPOSITORY_SERVICE_GUEST_USER:-}" REPOSITORY_SERVICE_HOME_APPID: "${REPOSITORY_SERVICE_HOME_APPID:-${COMPOSE_PROJECT_NAME:-local}}" + REPOSITORY_SERVICE_HOME_CUSTOM_HTML_HEADERS: "${REPOSITORY_SERVICE_HOME_CUSTOM_HTML_HEADERS:-}" REPOSITORY_SERVICE_HOME_AUTH: "${REPOSITORY_SERVICE_AUTH:-}" REPOSITORY_SERVICE_HOME_AUTH_EXTERNAL: "${REPOSITORY_SERVICE_AUTH_EXTERNAL:-false}" REPOSITORY_SERVICE_HOME_AUTH_EXTERNAL_LOGIN: "${REPOSITORY_SERVICE_AUTH_EXTERNAL_LOGIN:-}" diff --git a/deploy/docker/helm/search-solr/src/main/chart/templates/statefulset.yaml b/deploy/docker/helm/search-solr/src/main/chart/templates/statefulset.yaml index d9b85a052e..8b9c740eb6 100644 --- a/deploy/docker/helm/search-solr/src/main/chart/templates/statefulset.yaml +++ b/deploy/docker/helm/search-solr/src/main/chart/templates/statefulset.yaml @@ -260,7 +260,7 @@ spec: {{- if (default $.Values.global.cluster.storage.data.permission $.Values.persistence.data.permission) }} initContainers: - name: {{ include "edusharing_common_lib.names.name" $ }}-init-permission - image: {{ include "edusharing_common_lib.images.common" $ }}{{ .Values.init.permission.image.name }}:{{ .Values.init.permission.image.tag }} + image: {{ include "edusharing_common_lib.images.common" $ }}{{ $.Values.init.permission.image.name }}:{{ $.Values.init.permission.image.tag }} imagePullPolicy: {{ $.Values.global.image.pullPolicy }} command: - "/bin/bash" @@ -319,8 +319,25 @@ spec: {{- end }} containers: - name: {{ include "edusharing_common_lib.names.name" $ }} - image: {{ include "edusharing_common_lib.images.common" $ }}{{ .Values.image.name }}:{{ .Values.image.tag }} + image: {{ include "edusharing_common_lib.images.common" $ }}{{ $.Values.image.name }}:{{ $.Values.image.tag }} imagePullPolicy: {{ $.Values.global.image.pullPolicy }} + args: + - solr/solr/bin/solr + - start + - '-f' + - '-p' + - '8080' + {{- if or $.Values.config.metrics.enabled (default $.Values.global.debug $.Values.debug) }} + - '-a' + - >- + {{- if $.Values.config.metrics.enabled }} + -javaagent:/opt/alfresco/solr/agent/jmx_prometheus_javaagent.jar=9090:/opt/alfresco/solr/agent/jmx-exporter-config.yaml + {{- end }} + {{- if default $.Values.global.debug $.Values.debug }} + -agentlib:jdwp=transport=dt_socket,address=0.0.0.0:5005,server=y,suspend=n + {{- end }} + {{- end }} + - '-Dcreate.alfresco.defaults=alfresco,archive' env: - name: REPOSITORY_SEARCH_SOLR_CONFIG value: | {{ .spec.config.override.application.common | nindent 12 }} diff --git a/deploy/docker/helm/service/src/main/chart/README.md b/deploy/docker/helm/service/src/main/chart/README.md index fbb611326e..1c99543609 100644 --- a/deploy/docker/helm/service/src/main/chart/README.md +++ b/deploy/docker/helm/service/src/main/chart/README.md @@ -48,6 +48,7 @@ | `service.port.gossip` | Set port for service gossip | `5701` | | `service.port.metrics.api` | Set port for metrics API | `9090` | | `service.port.metrics.proxy` | Set port for metrics proxy | `9091` | +| `ingress.enabled` | Enable ingress | `true` | | `ingress.annotations.kubernetes.io/ingress.class` | Set kubernetes ingress class name | `{"kubernetes.io/ingress.class":"nginx","nginx.ingress.kubernetes.io/affinity":"cookie","nginx.ingress.kubernetes.io/app-root":"/edu-sharing","nginx.ingress.kubernetes.io/proxy-body-size":"5g","nginx.ingress.kubernetes.io/proxy-connect-timeout":"60","nginx.ingress.kubernetes.io/proxy-read-timeout":"180","nginx.ingress.kubernetes.io/proxy-send-timeout":"180"}` | | `ingress.annotations.nginx.ingress.kubernetes.io/affinity` | Set affinity on nginx ingress | `{"kubernetes.io/ingress.class":"nginx","nginx.ingress.kubernetes.io/affinity":"cookie","nginx.ingress.kubernetes.io/app-root":"/edu-sharing","nginx.ingress.kubernetes.io/proxy-body-size":"5g","nginx.ingress.kubernetes.io/proxy-connect-timeout":"60","nginx.ingress.kubernetes.io/proxy-read-timeout":"180","nginx.ingress.kubernetes.io/proxy-send-timeout":"180"}` | | `ingress.annotations.nginx.ingress.kubernetes.io/app-root` | Set app-root on nginx ingress | `{"kubernetes.io/ingress.class":"nginx","nginx.ingress.kubernetes.io/affinity":"cookie","nginx.ingress.kubernetes.io/app-root":"/edu-sharing","nginx.ingress.kubernetes.io/proxy-body-size":"5g","nginx.ingress.kubernetes.io/proxy-connect-timeout":"60","nginx.ingress.kubernetes.io/proxy-read-timeout":"180","nginx.ingress.kubernetes.io/proxy-send-timeout":"180"}` | @@ -78,6 +79,7 @@ | `config.cluster.enabled` | Enable clustering | `false` | | `config.cluster.backup.count` | Set cluster backup count | `1` | | `config.cluster.backup.read` | Set cluster backup as read | `false` | +| `config.connector.accesslog` | Enable access log | `false` | | `config.connector.external.threads` | Set external connector threads | `200` | | `config.connector.external.timeout` | Set external connector timeout | `20000` | | `config.connector.internal.threads` | Set internal connector threads | `200` | @@ -104,6 +106,7 @@ | `config.home.appid` | Set home app id | `local` | | `config.home.cookieAttributes` | Set home cookie attributes | `""` | | `config.home.urlDynamic` | Set url_dynamic attribute | `false` | +| `config.home.customHtmlHeaders` | Set home custom_html_headers attributes | `""` | | `config.jvm.ram.minPercentage` | Set minimum memory in percentages | `75.0` | | `config.jvm.ram.maxPercentage` | Set maximum memory in percentages | `75.0` | | `config.metrics.enabled` | Enable metrics | `true` | @@ -163,14 +166,23 @@ | `debug` | Enable debugging | `false` | | `persistence.data.config.spec.accessModes` | Set access modes for persistent data | `["ReadWriteOnce"]` | | `persistence.data.config.spec.resources.requests.storage` | Set storage request for persistent data | `1Gi` | -| `persistence.share.config.create` | Create persistent share | `true` | -| `persistence.share.config.spec.accessModes` | Set access modes for persistent share | `["ReadWriteMany"]` | -| `persistence.share.config.spec.resources.requests.storage` | Set storage request for persistent share | `1Gi` | +| `persistence.share.config.create` | Create persistent config share | `true` | +| `persistence.share.config.type` | Set volume type of persistent config share | `persistentVolumeClaim: + claimName: {{ include "edusharing_repository_service.pvc.share.config" . }} +` | +| `persistence.share.config.spec.accessModes` | Set access modes for persistent config share | `["ReadWriteMany"]` | +| `persistence.share.config.spec.resources.requests.storage` | Set storage request for persistent config share | `1Gi` | | `persistence.share.data.create` | Create persistent data share | `true` | +| `persistence.share.data.type` | Set volume type of persistent data share | `persistentVolumeClaim: + claimName: {{ include "edusharing_repository_service.pvc.share.data" . }} +` | | `persistence.share.data.spec.accessModes` | Set access modes for persistent data share | `["ReadWriteMany"]` | | `persistence.share.data.spec.resources.requests.storage` | Set storage request for persistent data share | `5Gi` | -| `persistence.share.safe.enabled` | Enable safe data share | `false` | -| `persistence.share.safe.create` | Create safe data share | `true` | +| `persistence.share.safe.enabled` | Enable persistent safe share | `false` | +| `persistence.share.safe.create` | Create persistent safe share | `true` | +| `persistence.share.safe.type` | Set volume type of persistent safe share | `persistentVolumeClaim: + claimName: {{ include "edusharing_repository_service.pvc.share.safe" . }} +` | | `persistence.share.safe.spec.accessModes` | Set access modes for persistent safe share | `["ReadWriteMany"]` | | `persistence.share.safe.spec.resources.requests.storage` | Set storage request for persistent safe share | `5Gi` | | `nodeAffinity` | Set node affinity | `{}` | diff --git a/deploy/docker/helm/service/src/main/chart/templates/configmap-env.yaml b/deploy/docker/helm/service/src/main/chart/templates/configmap-env.yaml index 1a69a8aa42..50d8b664e1 100644 --- a/deploy/docker/helm/service/src/main/chart/templates/configmap-env.yaml +++ b/deploy/docker/helm/service/src/main/chart/templates/configmap-env.yaml @@ -66,9 +66,11 @@ data: REPOSITORY_SERVICE_HOME_URL_DYNAMIC: {{ .Values.config.home.urlDynamic | quote }} REPOSITORY_SERVICE_HOME_AUTH: {{ if or .Values.config.oidc.enabled .Values.config.saml.enabled }}"shibboleth"{{ else }}""{{ end }} REPOSITORY_SERVICE_HOME_COOKIE_ATTRIBUTES: {{ .Values.config.home.cookieAttributes | quote }} + REPOSITORY_SERVICE_HOME_CUSTOM_HTML_HEADERS: {{ .Values.config.home.customHtmlHeaders | quote }} REPOSITORY_SERVICE_ALLOW_ORIGIN: "{{- join "," .Values.config.http.server.allowOrigin}}" REPOSITORY_SERVICE_HOST_EXTERNAL: {{ .Values.ingress.hosts | first | quote }} REPOSITORY_SERVICE_HOST_INTERNAL: {{ printf "%s.%s.svc.%s" (include "edusharing_common_lib.names.name" .) .Release.Namespace .Values.global.cluster.domain | quote }} + REPOSITORY_SERVICE_HTTP_ACCESSLOG_ENABLED: "{{ if .Values.config.connector.accesslog }}true{{ end }}" REPOSITORY_SERVICE_HTTP_CLIENT_DISABLE_SNI4HOSTS: {{ .Values.config.http.client.disablesni4host | quote }} REPOSITORY_SERVICE_HTTP_CLIENT_PROXY_HOST: {{ .Values.config.http.client.proxy.host | quote }} REPOSITORY_SERVICE_HTTP_CLIENT_PROXY_NONPROXYHOSTS: "'{{- join "|" .Values.config.http.client.proxy.nonproxyhosts }}'" diff --git a/deploy/docker/helm/service/src/main/chart/templates/configmap-file.yaml b/deploy/docker/helm/service/src/main/chart/templates/configmap-file.yaml index da0d944cad..aac1227fae 100644 --- a/deploy/docker/helm/service/src/main/chart/templates/configmap-file.yaml +++ b/deploy/docker/helm/service/src/main/chart/templates/configmap-file.yaml @@ -50,7 +50,7 @@ data: return (synth(200, "OK")); } - if (req.method == "POST" || req.method == "PUT" || req.method == "DELETE" || req.url ~ "/rest/") { + if (req.method == "POST" || req.method == "PUT" || req.method == "DELETE" || req.url ~ "/rest/" || req.url ~ "/webdav/") { return (pass); } @@ -63,6 +63,10 @@ data: } } + sub vcl_pipe { + set bereq.http.Connection = "close"; + } + sub vcl_hit { if (obj.ttl >= 0s) { return (deliver); @@ -86,6 +90,9 @@ data: set beresp.http.cache-control = "{{ .Values.proxy.config.cache.control }}, max-age={{ .Values.proxy.config.cache.ttl }}"; {{- end }} return (deliver); + } else { + set beresp.ttl = 0s; + set beresp.http.Connection = "close"; } } diff --git a/deploy/docker/helm/service/src/main/chart/templates/ingress.yaml b/deploy/docker/helm/service/src/main/chart/templates/ingress.yaml index dc06536c2b..dc39e6535e 100644 --- a/deploy/docker/helm/service/src/main/chart/templates/ingress.yaml +++ b/deploy/docker/helm/service/src/main/chart/templates/ingress.yaml @@ -1,4 +1,5 @@ {{- if not .Values.global.cluster.istio.enabled }} +{{- if .Values.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -58,4 +59,5 @@ spec: number: {{ $.Values.service.port.api.external }} {{- end }} {{- end }} +{{- end }} {{- end }} \ No newline at end of file diff --git a/deploy/docker/helm/service/src/main/chart/templates/statefulset.yaml b/deploy/docker/helm/service/src/main/chart/templates/statefulset.yaml index b2dbc6ece0..3c99f73119 100644 --- a/deploy/docker/helm/service/src/main/chart/templates/statefulset.yaml +++ b/deploy/docker/helm/service/src/main/chart/templates/statefulset.yaml @@ -431,15 +431,12 @@ spec: {{- end }} volumes: - name: share-config - persistentVolumeClaim: - claimName: {{ include "edusharing_repository_service.pvc.share.config" . }} + {{ tpl .Values.persistence.share.config.type . | nindent 8 }} - name: share-data - persistentVolumeClaim: - claimName: {{ include "edusharing_repository_service.pvc.share.data" . }} + {{ tpl .Values.persistence.share.data.type . | nindent 8 }} {{- if .Values.persistence.share.safe.enabled }} - name: share-safe - persistentVolumeClaim: - claimName: {{ include "edusharing_repository_service.pvc.share.safe" . }} + {{ tpl .Values.persistence.share.safe.type . | nindent 8 }} {{- end }} - name: file configMap: diff --git a/deploy/docker/helm/service/src/main/chart/values.yaml b/deploy/docker/helm/service/src/main/chart/values.yaml index 4dfecff600..19eb503884 100644 --- a/deploy/docker/helm/service/src/main/chart/values.yaml +++ b/deploy/docker/helm/service/src/main/chart/values.yaml @@ -139,6 +139,9 @@ service: ingress: + ## @param ingress.enabled Enable ingress + enabled: true + annotations: ## @param ingress.annotations.kubernetes.io/ingress.class Set kubernetes ingress class name kubernetes.io/ingress.class: nginx @@ -233,6 +236,9 @@ config: connector: + ## @param config.connector.accesslog Enable access log + accesslog: false + external: ## @param config.connector.external.threads Set external connector threads threads: 200 @@ -309,6 +315,8 @@ config: cookieAttributes: "" ## @param config.home.urlDynamic Set url_dynamic attribute urlDynamic: false + ## @param config.home.customHtmlHeaders Set home custom_html_headers attributes + customHtmlHeaders: "" jvm: ram: @@ -488,18 +496,26 @@ persistence: share: config: - ## @param persistence.share.config.create Create persistent share + ## @param persistence.share.config.create Create persistent config share create: true + ## @param persistence.share.config.type Set volume type of persistent config share + type: | + persistentVolumeClaim: + claimName: {{ include "edusharing_repository_service.pvc.share.config" . }} spec: - ## @param persistence.share.config.spec.accessModes Set access modes for persistent share + ## @param persistence.share.config.spec.accessModes Set access modes for persistent config share accessModes: [ "ReadWriteMany" ] resources: requests: - ## @param persistence.share.config.spec.resources.requests.storage Set storage request for persistent share + ## @param persistence.share.config.spec.resources.requests.storage Set storage request for persistent config share storage: 1Gi data: ## @param persistence.share.data.create Create persistent data share create: true + ## @param persistence.share.data.type Set volume type of persistent data share + type: | + persistentVolumeClaim: + claimName: {{ include "edusharing_repository_service.pvc.share.data" . }} spec: ## @param persistence.share.data.spec.accessModes Set access modes for persistent data share accessModes: [ "ReadWriteMany" ] @@ -509,10 +525,14 @@ persistence: storage: 5Gi safe: - ## @param persistence.share.safe.enabled Enable safe data share + ## @param persistence.share.safe.enabled Enable persistent safe share enabled: false - ## @param persistence.share.safe.create Create safe data share + ## @param persistence.share.safe.create Create persistent safe share create: true + ## @param persistence.share.safe.type Set volume type of persistent safe share + type: | + persistentVolumeClaim: + claimName: {{ include "edusharing_repository_service.pvc.share.safe" . }} spec: ## @param persistence.share.safe.spec.accessModes Set access modes for persistent safe share accessModes: [ "ReadWriteMany" ] diff --git a/pom.xml b/pom.xml index 105872ca18..3fcbd74c45 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ do not update to 6.2.2 cause it breaks openidconnect backchannel logout https://github.com/spring-projects/spring-security/issues/14553 --> - 6.2.4 + 6.2.6 diff --git a/webapp/src/main/templates/WEB-INF/classes/version.json b/webapp/src/main/templates/WEB-INF/classes/repository-version.json similarity index 100% rename from webapp/src/main/templates/WEB-INF/classes/version.json rename to webapp/src/main/templates/WEB-INF/classes/repository-version.json