From 75e8b10e6c8bb6af7ad7286b5c6c5e89a4a2e24a Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 1 Feb 2017 20:34:43 +0000 Subject: [PATCH] CCM NG: - Removed EntityGraphs. They did not work as intended. - Moved operations on entities in CcmShiroRealm to separate class. The CcmShiroRealmController wraps these operations into a transaction to avoid LazyInititalizationExceptions. git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4548 8810af33-2d31-482b-a856-94f89814c4df Former-commit-id: e1b612a2fbd2790797ef178a0fb9614b076abe18 --- .../com/arsdigita/cms/ItemSelectionModel.java | 4 +- .../com/arsdigita/cms/dispatcher/CMSPage.java | 5 + .../arsdigita/cms/ui/ContentSectionPage.java | 13 +- .../cms/ui/search/ItemQueryComponent.java | 6 +- .../arsdigita/ui/CcmObjectSelectionModel.java | 6 +- .../org/libreccm/categorization/Category.java | 3 - .../org/libreccm/categorization/Domain.java | 2 - .../categorization/DomainRepository.java | 29 +--- .../core/AbstractEntityRepository.java | 49 +----- .../org/libreccm/core/DefaultEntityGraph.java | 39 ----- .../org/libreccm/security/CcmShiroRealm.java | 151 +++-------------- .../security/CcmShiroRealmController.java | 155 ++++++++++++++++++ .../java/org/libreccm/security/Group.java | 4 +- .../java/org/libreccm/security/Party.java | 4 +- .../main/java/org/libreccm/security/Role.java | 5 +- .../main/java/org/libreccm/security/User.java | 4 +- .../org/libreccm/security/UserRepository.java | 2 - .../portation/CoreDataImportTest.java | 4 +- 18 files changed, 209 insertions(+), 276 deletions(-) delete mode 100644 ccm-core/src/main/java/org/libreccm/core/DefaultEntityGraph.java create mode 100644 ccm-core/src/main/java/org/libreccm/security/CcmShiroRealmController.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java index 4d6310300..b47b4c0ee 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java @@ -54,11 +54,11 @@ public class ItemSelectionModel extends CcmObjectSelectionModel { private Long typeId; public ItemSelectionModel(final LongParameter parameter) { - super(parameter); + super(ContentItem.class.getName(), parameter); } public ItemSelectionModel(final String parameterName) { - super(parameterName); + super(ContentItem.class.getName(), parameterName); } /** diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java index 451640591..96cd8683b 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java @@ -24,6 +24,7 @@ import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Page; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.page.PageTransformer; +import com.arsdigita.cms.CMS; import com.arsdigita.dispatcher.RequestContext; import com.arsdigita.templating.PresentationManager; import com.arsdigita.web.Web; @@ -281,6 +282,10 @@ public class CMSPage extends Page implements ResourceHandler { section = (ContentSection) app; } + if (section != null) { + CMS.getContext().setContentSection(section); + } + final String itemId = request.getParameter("item_id"); if (itemId != null) { diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java index 8bc225acc..5db1d6c1c 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java @@ -243,6 +243,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener { } return m_browsePane; } + /** * Creates, and then caches, the search pane. Overriding this method to * return null will prevent this tab from appearing. @@ -386,16 +387,16 @@ public class ContentSectionPage extends CMSPage implements ActionListener { final TabbedPane pane = new TabbedPane(); //tab(pane, "cms.ui.folders", getFolderAdminPane()); -// ToDo NG tab(pane, "cms.ui.browse", getBrowsePane()); -// ToDo NG tab(pane, "cms.ui.search", getSearchPane()); -// ToDo NG tab(pane, "cms.ui.images", getImagesPane()); -// ToDo NG tab(pane, "cms.ui.roles", getRoleAdminPane()); + tab(pane, "cms.ui.browse", getBrowsePane()); + tab(pane, "cms.ui.search", getSearchPane()); +// ToDo NG replace with media tab tab(pane, "cms.ui.images", getImagesPane()); + tab(pane, "cms.ui.roles", getRoleAdminPane()); tab(pane, "cms.ui.workflows", getWorkflowAdminPane()); tab(pane, "cms.ui.lifecycles", getLifecycleAdminPane()); -// ToDo NG tab(pane, "cms.ui.categories", getCategoryAdminPane()); +// ToDo NG tab(pane, "cms.ui.categories", getCategoryAdminPane()); tab(pane, "cms.ui.content_types", getContentTypeAdminPane()); tab(pane, "cms.ui.cse", getCSEPane()); -// ToDo NG tab(pane, "cms.ui.reports", getReportPane()); + tab(pane, "cms.ui.reports", getReportPane()); return pane; } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java index eb6f7a6d1..3252f95b1 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java @@ -118,9 +118,9 @@ public class ItemQueryComponent extends BaseQueryComponent { // CreationUserFilterType.KEY)); // add(new PartyFilterWidget(new LastModifiedUserFilterType(), // LastModifiedUserFilterType.KEY)); - Submit submit = new Submit(context + "_search", - ContentSectionPage.globalize("cms.ui.search")); - add(submit); +// Submit submit = new Submit(context + "_search", +// ContentSectionPage.globalize("cms.ui.search")); +// add(submit); } // private class LaunchDateFilterWidget extends DateRangeFilterWidget { diff --git a/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java b/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java index 6078cf9f6..7239bf1e3 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java @@ -42,11 +42,11 @@ public class CcmObjectSelectionModel private final SingleSelectionModel model; public CcmObjectSelectionModel(final LongParameter parameter) { - this("", parameter); + this(CcmObject.class.getName(), parameter); } public CcmObjectSelectionModel(final String parameterName) { - this("", new LongParameter(parameterName)); + this(CcmObject.class.getName(), new LongParameter(parameterName)); } // public CcmObjectSelectionModel(final SingleSelectionModel model ) { @@ -65,7 +65,7 @@ public class CcmObjectSelectionModel } catch (ClassNotFoundException ex) { throw new RuntimeException(ex); } - model = new ParameterSingleSelectionModel(new LongParameter( + model = new ParameterSingleSelectionModel(new LongParameter( parameterName)); } diff --git a/ccm-core/src/main/java/org/libreccm/categorization/Category.java b/ccm-core/src/main/java/org/libreccm/categorization/Category.java index 2d77e4cd4..66ac41678 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/Category.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/Category.java @@ -25,7 +25,6 @@ import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import org.hibernate.validator.constraints.NotBlank; import org.libreccm.core.CcmObject; -import org.libreccm.core.DefaultEntityGraph; import org.libreccm.l10n.LocalizedString; import org.libreccm.portation.Portable; import org.libreccm.security.RecursivePermissions; @@ -35,7 +34,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.Optional; import javax.persistence.AssociationOverride; import javax.persistence.Column; @@ -101,7 +99,6 @@ import javax.xml.bind.annotation.XmlRootElement; // } ) }) -@DefaultEntityGraph("Category.withSubCategoriesAndObjects") @XmlRootElement(name = "category", namespace = CAT_XML_NS) public class Category extends CcmObject implements Serializable, Portable { diff --git a/ccm-core/src/main/java/org/libreccm/categorization/Domain.java b/ccm-core/src/main/java/org/libreccm/categorization/Domain.java index f1a6054ea..37b00a0cc 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/Domain.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/Domain.java @@ -24,7 +24,6 @@ import static org.libreccm.core.CoreConstants.*; import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.URL; import org.libreccm.core.CcmObject; -import org.libreccm.core.DefaultEntityGraph; import org.libreccm.l10n.LocalizedString; import org.libreccm.security.RecursivePermissions; import org.libreccm.web.CcmApplication; @@ -110,7 +109,6 @@ import javax.xml.bind.annotation.XmlRootElement; } ) }) -@DefaultEntityGraph("Domain.allCategories") @XmlRootElement(name = "domain", namespace = CAT_XML_NS) public class Domain extends CcmObject implements Serializable { diff --git a/ccm-core/src/main/java/org/libreccm/categorization/DomainRepository.java b/ccm-core/src/main/java/org/libreccm/categorization/DomainRepository.java index 6f1404596..976be720d 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/DomainRepository.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/DomainRepository.java @@ -19,7 +19,6 @@ package org.libreccm.categorization; import org.libreccm.core.AbstractEntityRepository; -import org.libreccm.core.DefaultEntityGraph; import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.RequiresPrivilege; @@ -64,31 +63,8 @@ public class DomainRepository extends AbstractEntityRepository { @Override @Transactional(Transactional.TxType.REQUIRED) public List findAll() { - if (getEntityClass().isAnnotationPresent(DefaultEntityGraph.class)) { - return findAll(getEntityClass().getAnnotation( - DefaultEntityGraph.class).value()); - } else { - final TypedQuery query = getEntityManager() - .createNamedQuery("Domain.findAll", Domain.class); - return query.getResultList(); - } - } - - @Override - @Transactional(Transactional.TxType.REQUIRED) - public List findAll(final String entityGraphName) { - @SuppressWarnings("unchecked") - final EntityGraph entityGraph = (EntityGraph) entityManager. - getEntityGraph(entityGraphName); - return findAll(entityGraph); - } - - @Override - @Transactional(Transactional.TxType.REQUIRED) - public List findAll(final EntityGraph entityGraph) { final TypedQuery query = getEntityManager() - .createNamedQuery("Domain.findAll", Domain.class); - query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph); + .createNamedQuery("Domain.findAll", Domain.class); return query.getResultList(); } @@ -150,7 +126,7 @@ public class DomainRepository extends AbstractEntityRepository { public void save(final Domain domain) { super.save(domain); } - + @AuthorizationRequired @RequiresPrivilege(CategorizationConstants.PRIVILEGE_MANAGE_DOMAINS) @Transactional(Transactional.TxType.REQUIRED) @@ -158,4 +134,5 @@ public class DomainRepository extends AbstractEntityRepository { public void delete(final Domain domain) { super.delete(domain); } + } diff --git a/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java b/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java index c7f2c750d..1f46e3909 100644 --- a/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java +++ b/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java @@ -79,25 +79,6 @@ public abstract class AbstractEntityRepository { return entityManager.createEntityGraph(getEntityClass()); } - protected void applyDefaultEntityGraph(final TypedQuery query) { - if (getEntityClass().isAnnotationPresent(DefaultEntityGraph.class)) { - LOGGER.debug("The following EntityGraphs are available for the " - + "entity class {}:", - getEntityClass().getName()); - getEntityManager().getEntityGraphs(getEntityClass()).stream() - .forEach(g -> LOGGER.debug("\t{}", g.getName())); - LOGGER.debug("Entity class {} has default entity graphs:", - getEntityClass().getName()); - LOGGER.debug("Applying entity graph {}", - getEntityClass().getAnnotation( - DefaultEntityGraph.class).value()); - query.setHint(FETCH_GRAPH_HINT_KEY, - entityManager.getEntityGraph( - getEntityClass().getAnnotation( - DefaultEntityGraph.class).value())); - } - } - /** * Helper method for retrieving a single result from a query. * @@ -172,12 +153,7 @@ public abstract class AbstractEntityRepository { */ @Transactional(Transactional.TxType.REQUIRED) public E findById(final K entityId) { - if (getEntityClass().isAnnotationPresent(DefaultEntityGraph.class)) { - return findById(entityId, getEntityClass().getAnnotation( - DefaultEntityGraph.class).value()); - } else { - return entityManager.find(getEntityClass(), entityId); - } + return entityManager.find(getEntityClass(), entityId); } @Transactional(Transactional.TxType.REQUIRED) @@ -239,12 +215,8 @@ public abstract class AbstractEntityRepository { } public List executeCriteriaQuery(final CriteriaQuery criteriaQuery) { - if (hasDefaultEntityGraph()) { - return executeCriteriaQuery(criteriaQuery, getDefaultEntityGraph()); - } else { - final TypedQuery query = entityManager.createQuery(criteriaQuery); - return query.getResultList(); - } + final TypedQuery query = entityManager.createQuery(criteriaQuery); + return query.getResultList(); } public List executeCriteriaQuery(final CriteriaQuery criteriaQuery, @@ -316,19 +288,4 @@ public abstract class AbstractEntityRepository { entityManager.remove(entityManager.merge(entity)); } - protected boolean hasDefaultEntityGraph() { - return getEntityClass().isAnnotationPresent(DefaultEntityGraph.class); - } - - protected String getDefaultEntityGraph() { - if (hasDefaultEntityGraph()) { - return getEntityClass().getAnnotation(DefaultEntityGraph.class) - .value(); - } else { - throw new IllegalArgumentException(String.format( - "Entity class \"%s\" has no DefaultEntityGraph!", - getEntityClass().getName())); - } - } - } diff --git a/ccm-core/src/main/java/org/libreccm/core/DefaultEntityGraph.java b/ccm-core/src/main/java/org/libreccm/core/DefaultEntityGraph.java deleted file mode 100644 index 2b90c28bf..000000000 --- a/ccm-core/src/main/java/org/libreccm/core/DefaultEntityGraph.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016 LibreCCM Foundation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -package org.libreccm.core; - -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.*; - -/** - * - * @author Jens Pelzetter - */ -@Retention(RetentionPolicy.RUNTIME) -@Inherited -@Target({TYPE}) -public @interface DefaultEntityGraph { - - String value(); - -} diff --git a/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java b/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java index b2e9914fa..a88dc5f8c 100644 --- a/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java +++ b/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java @@ -18,7 +18,6 @@ */ package org.libreccm.security; -import com.arsdigita.kernel.KernelConfig; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; @@ -47,6 +46,15 @@ public class CcmShiroRealm extends AuthorizingRealm { protected AuthorizationInfo doGetAuthorizationInfo( final PrincipalCollection principals) { + final CcmShiroRealmController controller; + try { + controller = CdiUtil.createCdiUtil() + .findBean(CcmShiroRealmController.class); + } catch (IllegalStateException ex) { + throw new AuthenticationException( + "Failed to retrieve CcmShiroRealmController", ex); + } + // Get the pricipal (object identifing the user). final Object principal = principals.getPrimaryPrincipal(); @@ -64,35 +72,8 @@ public class CcmShiroRealm extends AuthorizingRealm { if ("system-user".equals(userIdentifier)) { // The system user is a virtual user which has all roles and all // privileges -// final RoleRepository roleRepository; -// final BeanManager beanManager = CDI.current().getBeanManager(); -// final Set> beans = beanManager. -// getBeans(RoleRepository.class); -// final Iterator> iterator = beans.iterator(); -// if (iterator.hasNext()) { -// @SuppressWarnings("unchecked") -// final Bean bean -// = (Bean) iterator -// .next(); -// final CreationalContext ctx = beanManager. -// createCreationalContext(bean); -// -// roleRepository = (RoleRepository) beanManager.getReference( -// bean, RoleRepository.class, ctx); -// } else { -// throw new AuthenticationException( -// "Failed to retrieve RoleRepository"); -// } - final RoleRepository roleRepository; - try { - roleRepository = CdiUtil.createCdiUtil() - .findBean(RoleRepository.class); - } catch (IllegalStateException ex) { - throw new AuthenticationException( - "Failed to retrieve RoleRepository", ex); - } - final List roles = roleRepository.findAll(); + final List roles = controller.retrieveAllRoles(); final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (final Role role : roles) { @@ -103,41 +84,7 @@ public class CcmShiroRealm extends AuthorizingRealm { return info; } - //Find the user identified by the provided pricipal. - final User user = findUser(userIdentifier); - - // Create a SimpleAuthorizationInfo instance. Contains the information - // from the database in the format expected by Shiro. - final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); - // Get the Roles directly assigned to the user. - for (final RoleMembership roleMembership : user.getRoleMemberships()) { - // Add the role to the AuthorizationInfo object. - info.addRole(roleMembership.getRole().getName()); - - // Add the permissions assigned to the role to the AuthorizatonInfo. - for (final Permission permission : roleMembership.getRole() - .getPermissions()) { - info.addStringPermission(permissionToString(permission)); - } - } - - //Get the Roles assigned to the groups of which the user is member of. - for (final GroupMembership membership : user.getGroupMemberships()) { - // Get the roles assigned to the group - for (final RoleMembership roleMembership : membership.getGroup() - .getRoleMemberships()) { - // Add the role to the AuthorizationInfo - info.addRole(roleMembership.getRole().getName()); - // Add the permissions assigned to the role to the - // AuthorizationInfo - for (final Permission permission : roleMembership.getRole() - .getPermissions()) { - info.addStringPermission(permissionToString(permission)); - } - } - } - - return info; + return controller.createAuthorizationInfo(userIdentifier); } @Override @@ -156,10 +103,19 @@ public class CcmShiroRealm extends AuthorizingRealm { principal.getClass().getName())); } + final CcmShiroRealmController controller; + try { + controller = CdiUtil.createCdiUtil() + .findBean(CcmShiroRealmController.class); + } catch (IllegalStateException ex) { + throw new AuthenticationException( + "Failed to retrieve CcmShiroRealmController", ex); + } + // Convert the pricipal to a string. final String userIdentifier = (String) principal; // Find the user identified by the pricipal. - final User user = findUser(userIdentifier); + final User user = controller.findUser(userIdentifier); // Return a SimpleAuthenticationInfo with the information relevant // for Shiro @@ -168,71 +124,6 @@ public class CcmShiroRealm extends AuthorizingRealm { "CcmShiroRealm"); } - /** - * Helper method for finding a user by its identifier. Depending on the - * configuration of CCM this is either the name of the user or the email - * address of the user. - * - * @param userIdentifier The identifier of the user. - * - * @return The User identified by the provided {@code userIdentifier}. - * - * @throws AuthenticationException if no user for the provided identifier - * could be retrieved. - */ - private User findUser(final String userIdentifier) { - // For some reason we can't use the the CdiUtil class here, therefore - // we have to do the lookup for the UserRepository be ourself. - final UserRepository userRepository; -// final BeanManager beanManager = CDI.current().getBeanManager(); -// final Set> beans = beanManager.getBeans( -// UserRepository.class); -// final Iterator> iterator = beans.iterator(); -// if (iterator.hasNext()) { -// @SuppressWarnings("unchecked") -// final Bean bean = (Bean) iterator -// .next(); -// final CreationalContext ctx = beanManager -// .createCreationalContext(bean); -// -// userRepository = (UserRepository) beanManager.getReference( -// bean, UserRepository.class, ctx); - try { - userRepository = CdiUtil.createCdiUtil().findBean( - UserRepository.class); - } catch (IllegalStateException ex) { - throw new AuthenticationException( - "Failed to retrieve UserRepository.", ex); - } - -// } else { -// throw new AuthenticationException( -// "Failed to retrieve UserRepository."); -// } - // Depending of the configuration of CCM use the appropriate method - // for finding the user in the database. - final KernelConfig config = KernelConfig.getConfig(); - final User user; - - if ("email".equals(config.getPrimaryUserIdentifier())) { - user = userRepository.findByEmailAddress(userIdentifier); - } else { - user = userRepository.findByName(userIdentifier); - } - - // If no matching user is found throw an AuthenticationException - if (user - == null) { - throw new AuthenticationException(String.format( - "No user identified by principal \"%s\" was found. Primary user " - + "identifier is \"%s\".", - userIdentifier, config.getPrimaryUserIdentifier())); - } - - return user; - - } - /** * Helper method for converting a {@link Permission} to the string format * used by Shiro. diff --git a/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealmController.java b/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealmController.java new file mode 100644 index 000000000..ea28b3edf --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealmController.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.security; + +import com.arsdigita.kernel.KernelConfig; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.libreccm.configuration.ConfigurationManager; + +import java.util.List; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.transaction.Transactional; + +/** + * This bean provides several services for the {@link CcmShiroRealm}. It wraps + * several calls into a transaction to avoid + * {@code LazyInitializationException}. + * + * @author Jens Pelzetter + */ +@RequestScoped +class CcmShiroRealmController { + + @Inject + private UserRepository userRepo; + + @Inject + private GroupRepository groupRepo; + + @Inject + private RoleRepository roleRepo; + + @Inject + private ConfigurationManager confManager; + + @Transactional(Transactional.TxType.REQUIRED) + protected List retrieveAllRoles() { + return roleRepo.findAll(); + } + + /** + * Helper method for finding a user by its identifier. Depending on the + * configuration of CCM this is either the name of the user or the email + * address of the user. + * + * @param userIdentifier The identifier of the user. + * + * @return The User identified by the provided {@code userIdentifier}. + * + * @throws AuthenticationException if no user for the provided identifier + * could be retrieved. + */ + @Transactional(Transactional.TxType.REQUIRED) + protected User findUser(final String userIdentifier) { + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class); + + final User user; + if ("email".equals(kernelConfig.getPrimaryUserIdentifier())) { + user = userRepo.findByEmailAddress(userIdentifier); + } else { + user = userRepo.findByName(userIdentifier); + } + + // If no matching user is found throw an AuthenticationException + if (user + == null) { + throw new AuthenticationException(String.format( + "No user identified by principal \"%s\" was found. Primary user " + + "identifier is \"%s\".", + userIdentifier, kernelConfig.getPrimaryUserIdentifier())); + } + + return user; + } + + @Transactional(Transactional.TxType.REQUIRED) + protected AuthorizationInfo createAuthorizationInfo( + final String userIdentifier) { + + final User user = findUser(userIdentifier); + + // Create a SimpleAuthorizationInfo instance. Contains the information + // from the database in the format expected by Shiro. + final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + // Get the Roles directly assigned to the user. + for (final RoleMembership roleMembership : user.getRoleMemberships()) { + // Add the role to the AuthorizationInfo object. + info.addRole(roleMembership.getRole().getName()); + + // Add the permissions assigned to the role to the AuthorizatonInfo. + for (final Permission permission : roleMembership.getRole() + .getPermissions()) { + info.addStringPermission(permissionToString(permission)); + } + } + + //Get the Roles assigned to the groups of which the user is member of. + for (final GroupMembership membership : user.getGroupMemberships()) { + // Get the roles assigned to the group + for (final RoleMembership roleMembership : membership.getGroup() + .getRoleMemberships()) { + // Add the role to the AuthorizationInfo + info.addRole(roleMembership.getRole().getName()); + // Add the permissions assigned to the role to the + // AuthorizationInfo + for (final Permission permission : roleMembership.getRole() + .getPermissions()) { + info.addStringPermission(permissionToString(permission)); + } + } + } + + return info; + } + + /** + * Helper method for converting a {@link Permission} to the string format + * used by Shiro. + * + * @param permission The permission to convert. + * + * @return A Shiro permission string. + */ + private String permissionToString(final Permission permission) { + if (permission.getObject() == null) { + return permission.getGrantedPrivilege(); + } else { + return String.format("%s:%d", + permission.getGrantedPrivilege(), + permission.getObject().getObjectId()); + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/security/Group.java b/ccm-core/src/main/java/org/libreccm/security/Group.java index f723d9f83..6ba0ffe31 100644 --- a/ccm-core/src/main/java/org/libreccm/security/Group.java +++ b/ccm-core/src/main/java/org/libreccm/security/Group.java @@ -18,9 +18,7 @@ */ package org.libreccm.security; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; -import org.libreccm.core.DefaultEntityGraph; import org.libreccm.portation.Portable; import javax.persistence.Entity; @@ -35,6 +33,7 @@ import javax.persistence.Table; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; + import java.io.Serializable; import java.util.Collections; import java.util.HashSet; @@ -89,7 +88,6 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA; @NamedAttributeNode(value = "permissions")}) }) }) -@DefaultEntityGraph("Group.withMembersAndRoleMemberships") @XmlRootElement(name = "user-group", namespace = CORE_XML_NS) public class Group extends Party implements Serializable, Portable { diff --git a/ccm-core/src/main/java/org/libreccm/security/Party.java b/ccm-core/src/main/java/org/libreccm/security/Party.java index 75e31b9d6..fac24c0e5 100644 --- a/ccm-core/src/main/java/org/libreccm/security/Party.java +++ b/ccm-core/src/main/java/org/libreccm/security/Party.java @@ -18,9 +18,7 @@ */ package org.libreccm.security; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; -import org.libreccm.core.DefaultEntityGraph; import javax.persistence.Column; import javax.persistence.Entity; @@ -40,6 +38,7 @@ import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; + import java.io.Serializable; import java.util.Collections; import java.util.HashSet; @@ -74,7 +73,6 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA; attributeNodes = @NamedAttributeNode( value = "roleMemberships")) }) -@DefaultEntityGraph("Party.withRoleMemberships") public class Party implements Serializable { private static final long serialVersionUID = 3319997992281332204L; diff --git a/ccm-core/src/main/java/org/libreccm/security/Role.java b/ccm-core/src/main/java/org/libreccm/security/Role.java index 560201faf..0af47176b 100644 --- a/ccm-core/src/main/java/org/libreccm/security/Role.java +++ b/ccm-core/src/main/java/org/libreccm/security/Role.java @@ -18,10 +18,8 @@ */ package org.libreccm.security; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import org.hibernate.validator.constraints.NotBlank; -import org.libreccm.core.DefaultEntityGraph; import org.libreccm.l10n.LocalizedString; import org.libreccm.portation.Portable; import org.libreccm.workflow.TaskAssignment; @@ -48,6 +46,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -55,6 +54,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; + import org.hibernate.search.annotations.Field; import static org.libreccm.core.CoreConstants.CORE_XML_NS; @@ -117,7 +117,6 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA; @NamedAttributeNode(value = "permissions") }) }) -@DefaultEntityGraph(Role.ENTITY_GRPAH_WITH_MEMBERS) @XmlRootElement(name = "role", namespace = CORE_XML_NS) @XmlAccessorType(XmlAccessType.FIELD) @SuppressWarnings({"PMD.ShortClassName", "PMD.TooManyMethods"}) diff --git a/ccm-core/src/main/java/org/libreccm/security/User.java b/ccm-core/src/main/java/org/libreccm/security/User.java index 5bc5e6a72..c7a437b8e 100644 --- a/ccm-core/src/main/java/org/libreccm/security/User.java +++ b/ccm-core/src/main/java/org/libreccm/security/User.java @@ -18,9 +18,7 @@ */ package org.libreccm.security; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; -import org.libreccm.core.DefaultEntityGraph; import org.libreccm.core.EmailAddress; import org.libreccm.portation.Portable; @@ -48,6 +46,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -122,7 +121,6 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA; ) }) }) -@DefaultEntityGraph("User.withGroupAndRoleMemberships") @XmlRootElement(name = "user", namespace = CORE_XML_NS) @XmlAccessorType(XmlAccessType.FIELD) //Supressing a few warnings from PMD because they misleading here. diff --git a/ccm-core/src/main/java/org/libreccm/security/UserRepository.java b/ccm-core/src/main/java/org/libreccm/security/UserRepository.java index 8c8fea5d6..c33520483 100644 --- a/ccm-core/src/main/java/org/libreccm/security/UserRepository.java +++ b/ccm-core/src/main/java/org/libreccm/security/UserRepository.java @@ -61,7 +61,6 @@ public class UserRepository extends AbstractEntityRepository { public User findByName(final String name) { final TypedQuery query = getEntityManager().createNamedQuery( "User.findByName", User.class); - applyDefaultEntityGraph(query); query.setParameter("name", name); return getSingleResultOrNull(query); @@ -123,7 +122,6 @@ public class UserRepository extends AbstractEntityRepository { final TypedQuery query = getEntityManager().createNamedQuery( "User.findByEmailAddress", User.class); query.setParameter("emailAddress", emailAddress); - applyDefaultEntityGraph(query); return getSingleResultOrNull(query); } diff --git a/ccm-core/src/test/java/org/libreccm/portation/CoreDataImportTest.java b/ccm-core/src/test/java/org/libreccm/portation/CoreDataImportTest.java index 042e68de9..240581a81 100644 --- a/ccm-core/src/test/java/org/libreccm/portation/CoreDataImportTest.java +++ b/ccm-core/src/test/java/org/libreccm/portation/CoreDataImportTest.java @@ -19,6 +19,7 @@ package org.libreccm.portation; import javax.inject.Inject; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.junit.InSequence; @@ -35,7 +36,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.libreccm.tests.categories.IntegrationTest; import static org.libreccm.testutils.DependenciesHelpers.getModuleDependencies; @@ -49,7 +49,7 @@ import org.jboss.arquillian.persistence.CleanupUsingScript; * @author Tobias Osmers * @version created on 12/1/16 */ -@org.junit.experimental.categories.Category(IntegrationTest.class) +//@org.junit.experimental.categories.Category(IntegrationTest.class) @RunWith(Arquillian.class) @PersistenceTest @Transactional(TransactionMode.COMMIT)