diff --git a/ccm-cms/src/main/java/org/librecms/pages/PagesController.java b/ccm-cms/src/main/java/org/librecms/pages/PagesController.java index 973a22bd9..64c1d90e1 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/PagesController.java +++ b/ccm-cms/src/main/java/org/librecms/pages/PagesController.java @@ -580,16 +580,28 @@ public class PagesController { final Page page = pageManager.findPageForCategory(category); pagePropertiesModel.setProperties(page.getProperties()); - if (itemName.equals("index") || itemName.isBlank()) { - return themesMvc.getMvcTemplate( - uriInfo, "pages", page.getDisplayName() - ); - } else { - return themesMvc.getMvcTemplate( - uriInfo, "pages", "item-page" + try { + final String result; + if (itemName.equals("index") || itemName.isBlank()) { + result = themesMvc.getMvcTemplate( + uriInfo, "pages", page.getDisplayName() + ); + + } else { + result = themesMvc.getMvcTemplate( + uriInfo, "pages", "item-page" + ); + } + return result; + } catch (Exception ex) { + throw new WebApplicationException( + "An error occured while rendering the page.", + ex, + Response.serverError().entity( + "An error occured while rendering the page." + ).build() ); } - } private void initSiteInfoModel( diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemListItemModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemListItemModel.java new file mode 100644 index 000000000..2afd65cd9 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemListItemModel.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.librecms.pages.models; + +import org.librecms.contentsection.ContentItem; + +/** + * + * @author Jens Pelzetter + */ +public class ContentItemListItemModel extends AbstractContentItemListItemModel { + + @Override + public String getType() { + return ContentItem.class.getName(); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemListModelBuilder.java b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemListModelBuilder.java new file mode 100644 index 000000000..1e8434846 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemListModelBuilder.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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.librecms.pages.models; + +import org.librecms.contentsection.ContentItem; + +import javax.enterprise.context.RequestScoped; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class ContentItemListModelBuilder + extends AbstractContentItemListItemModelBuilder { + + @Override + public Class buildsListItemModelFor() { + return ContentItem.class; + } + + @Override + protected ContentItemListItemModel buildModel() { + return new ContentItemListItemModel(); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ItemListModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/ItemListModel.java index a391f8e78..b9fde5b9b 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/ItemListModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ItemListModel.java @@ -251,10 +251,11 @@ public class ItemListModel { criteriaBuilder.isNull(catJoin.get("type")), criteriaBuilder.equal( from.get("version"), ContentItemVersion.LIVE - ), - buildPermissionsCheck( - criteriaBuilder, from, permissionsJoin ) +// , +// buildPermissionsCheck( +// criteriaBuilder, from, permissionsJoin +// ) ) ); @@ -265,7 +266,7 @@ public class ItemListModel { .collect(Collectors.toList()) ); - return entityManager + final List result = entityManager .createQuery(criteriaQuery) .getResultList() .stream() @@ -278,6 +279,8 @@ public class ItemListModel { .limit(pageSize) .map(this::buildListItemModel) .collect(Collectors.toList()); + + return result; } private List collectCategories(final Category category) { diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationController.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationController.java index 93ac282c0..de803fc9e 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationController.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationController.java @@ -93,6 +93,8 @@ public class ConfigurationController { return "org/librecms/ui/contentsection/configuration/index.xhtml"; } + + /** * Checks if the current user is permitted to access the configurations page * of the content section. diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationFixItemPermissionsController.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationFixItemPermissionsController.java new file mode 100644 index 000000000..a13f603df --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ConfigurationFixItemPermissionsController.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import org.libreccm.categorization.Categorization; +import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.PermissionManager; +import org.librecms.contentsection.Asset; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionManager; +import org.librecms.contentsection.Folder; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.mvc.Controller; +import javax.transaction.Transactional; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * Controller for the fix item and asset permissions function. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Controller +@Path("/{sectionIdentifier}/configuration/fix-item-and-asset-permissions") +public class ConfigurationFixItemPermissionsController { + + /** + * Used to check the admin permissions of a content section. + */ + @Inject + private AdminPermissionsChecker adminPermissionsChecker; + +// @Inject +// private ContentSectionManager sectionManager; + @Inject + private ContentSectionModel sectionModel; + + /** + * Common functions for views working with {@link ContentSection}s. + */ + @Inject + private ContentSectionsUi sectionsUi; + +// @Inject +// private PermissionManager permissionManager; +// + + @Inject + private FixItemPermissionsTaskManager taskManager; + + @POST + @Path("/") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String fixItemAndAssetPermissions( + @PathParam("sectionIdentifier") final String sectionIdentifierParam + ) { + final Optional sectionResult = sectionsUi + .findContentSection(sectionIdentifierParam); + if (!sectionResult.isPresent()) { + return sectionsUi.showContentSectionNotFound(sectionIdentifierParam); + } + final ContentSection section = sectionResult.get(); + sectionModel.setSection(section); + if (!adminPermissionsChecker.canAdministerContentTypes(section)) { + return sectionsUi.showAccessDenied( + "sectionIdentifier", sectionIdentifierParam + ); + } + +// fixContentItemPermissions(section.getRootDocumentsFolder()); +// fixAssetPermissions(section.getRootAssetsFolder()); + + taskManager.fixItemPermissions(section); + + return String.format( + "redirect:%s/configuration", sectionIdentifierParam + ); + } + +// private void fixContentItemPermissions(final Folder folder) { +// final List items = folder +// .getObjects() +// .stream() +// .map(Categorization::getCategorizedObject) +// .filter(obj -> obj instanceof ContentItem) +// .map(obj -> (ContentItem) obj) +// .collect(Collectors.toList()); +// +// for(final ContentItem item : items) { +// permissionManager.copyPermissions(folder, item, true); +// } +// +// for(final Folder subFolder : folder.getSubFolders()) { +// fixContentItemPermissions(subFolder); +// } +// } +// +// private void fixAssetPermissions(final Folder folder) { +// final List assets = folder +// .getObjects() +// .stream() +// .map(Categorization::getCategorizedObject) +// .filter(obj -> obj instanceof Asset) +// .map(obj -> (Asset) obj) +// .collect(Collectors.toList()); +// +// for(final Asset asset : assets) { +// permissionManager.copyPermissions(folder, asset, true); +// } +// +// for(final Folder subFolder : folder.getSubFolders()) { +// fixAssetPermissions(subFolder); +// } +// } +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java index 032ed66ec..f100f8eea 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java @@ -76,6 +76,7 @@ public class ContentSectionApplication extends Application { classes.add(ConfigurationController.class); classes.add(ConfigurationContactEntryKeysController.class); classes.add(ConfigurationDocumentTypesController.class); + classes.add(ConfigurationFixItemPermissionsController.class); classes.add(ConfigurationLifecyclesController.class); classes.add(ConfigurationRolesController.class); classes.add(ConfigurationWorkflowController.class); diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissions.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissions.java new file mode 100644 index 000000000..f03f9c62e --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissions.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.categorization.Categorization; +import org.libreccm.core.CcmObject; +import org.libreccm.security.PermissionManager; +import org.librecms.contentsection.Asset; +import org.librecms.contentsection.AssetManager; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionRepository; +import org.librecms.contentsection.Folder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.transaction.Transactional; + +/** + * + * @author Jens Pelzetter + */ +@ApplicationScoped +public class FixItemPermissions { + + private static final Logger LOGGER = LogManager.getLogger( + FixItemPermissions.class + ); + + @Inject + private AssetManager assetManager; + + @Inject + private ContentItemManager itemManager; + + @Inject + private ContentSectionRepository sectionRepo; + + @Inject + private PermissionManager permissionManager; + + @Transactional(Transactional.TxType.REQUIRED) + public void fixItemPermissions(final ContentSection contentSection) { + final String sectionUuid = Optional + .ofNullable(contentSection) + .map(ContentSection::getUuid) + .orElseThrow( + () -> new IllegalArgumentException( + "Can't fix ItemPermissions for content section null" + ) + ); + + final ContentSection section = sectionRepo + .findByUuid(sectionUuid) + .orElseThrow( + () -> new IllegalArgumentException( + String.format( + "No ContentSection with UUID %s in the database.", + sectionUuid + ) + ) + ); + + fixContentItemPermissions(section.getRootDocumentsFolder()); + fixAssetPermissions(section.getRootAssetsFolder()); + } + + private void fixContentItemPermissions(final Folder folder) { + final List items = new ArrayList<>(); + for (final Categorization categorization : folder.getObjects()) { + final CcmObject categorized = categorization.getCategorizedObject(); + if (categorized instanceof ContentItem) { + items.add((ContentItem) categorized); + } + } + +// final List items = folder +// .getObjects() +// .stream() +// .map(Categorization::getCategorizedObject) +// .filter(obj -> obj instanceof ContentItem) +// .map(obj -> (ContentItem) obj) +// .collect(Collectors.toList()); + for (final ContentItem item : items) { + LOGGER.info( + "Fixing permissions for item {} {}", + item.getUuid(), + itemManager.getItemPath(item) + ); + permissionManager.copyPermissions(folder, item, true); + + if (itemManager.isLive(item)) { + permissionManager.copyPermissions( + folder, + itemManager.getLiveVersion(item, ContentItem.class).get(), + true + ); + } + } + + for (final Folder subFolder : folder.getSubFolders()) { + fixContentItemPermissions(subFolder); + } + } + + private void fixAssetPermissions(final Folder folder) { + final List assets = folder + .getObjects() + .stream() + .map(Categorization::getCategorizedObject) + .filter(obj -> obj instanceof Asset) + .map(obj -> (Asset) obj) + .collect(Collectors.toList()); + + for (final Asset asset : assets) { + LOGGER.info( + "Fixing permissions for asset {} {}...", + asset.getUuid(), + assetManager.getAssetPath(asset) + ); + permissionManager.copyPermissions(folder, asset, true); + } + + for (final Folder subFolder : folder.getSubFolders()) { + fixAssetPermissions(subFolder); + } + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsStatus.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsStatus.java new file mode 100644 index 000000000..c493502d3 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsStatus.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +/** + * + * @author Jens Pelzetter + */ +public enum FixItemPermissionsStatus { + + /** + * An error occured during the process. + */ + ERROR, + /** + * The import or export task is finished. + */ + FINISHED, + /** + * The task is still running. + */ + RUNNING, + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTask.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTask.java new file mode 100644 index 000000000..be2a8f905 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTask.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import org.apache.shiro.mgt.SecurityManager; +import org.librecms.contentsection.ContentSection; + +import java.time.LocalDate; + +/** + * + * @author Jens Pelzetter + */ +public class FixItemPermissionsTask { + + private final ContentSection contentSection; + + private final LocalDate started; + + private final FixItemPermissionsTaskStatus status; + + private final org.apache.shiro.mgt.SecurityManager securityManager; + + public FixItemPermissionsTask( + final ContentSection contentSection, + final LocalDate started, + final FixItemPermissionsTaskStatus status, + final org.apache.shiro.mgt.SecurityManager securityManager + ) { + this.contentSection = contentSection; + this.started = started; + this.status = status; + this.securityManager = securityManager; + } + + public ContentSection getContentSection() { + return contentSection; + } + + public LocalDate getStarted() { + return started; + } + + public FixItemPermissionsTaskStatus getStatus() { + return status; + } + + public SecurityManager getSecurityManager() { + return securityManager; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskManager.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskManager.java new file mode 100644 index 000000000..1a1c6e541 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskManager.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.shiro.SecurityUtils; +import org.librecms.contentsection.ContentSection; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Event; +import javax.inject.Inject; +import javax.transaction.Transactional; + +/** + * + * @author Jens Pelzetter + */ +@ApplicationScoped +public class FixItemPermissionsTaskManager { + + private static final Logger LOGGER = LogManager.getLogger( + FixItemPermissionsTaskManager.class + ); + + @Inject + private Event taskSender; + + private final SortedSet tasks; + + public SortedSet getTasks() { + return Collections.unmodifiableSortedSet(tasks); + } + + public boolean isTaskActiveForContentSection(final ContentSection section) { + return isTaskActiveForContentSection( + Optional + .ofNullable(section) + .map(ContentSection::getLabel) + .orElseThrow( + () -> new IllegalArgumentException( + "Can't check task status for ContentSection null." + ) + ) + ); + } + + public boolean isTaskActiveForContentSection(final String name) { + return tasks + .stream() + .anyMatch( + task -> task.getContentSection().equals(name) + && task.getStatus() + == FixItemPermissionsStatus.RUNNING + ); + } + + public boolean isTaskFailedForContentSection(final String name) { + return tasks + .stream() + .anyMatch( + task -> task.getContentSection().equals(name) + && task.getStatus() + == FixItemPermissionsStatus.ERROR + ); + } + + public FixItemPermissionsTaskManager() { + tasks = new TreeSet<>( + Comparator + .comparing(FixItemPermissionsTaskStatus::getStarted) + .thenComparing(FixItemPermissionsTaskStatus::getContentSection) + ); + } + + @Transactional(Transactional.TxType.REQUIRED) + public void fixItemPermissions(final ContentSection section) { + if (isTaskActiveForContentSection(section)) { + throw new IllegalArgumentException( + String.format( + "A FixItemPermissionsTask is already active for content " + + "section %s.", + section.getLabel() + ) + ); + } + + tasks.removeAll( + tasks + .stream() + .filter(task -> task.getStatus() + == FixItemPermissionsStatus.ERROR + || task.getStatus() + == FixItemPermissionsStatus.FINISHED) + .collect(Collectors.toSet()) + ); + + final FixItemPermissionsTaskStatus taskStatus + = new FixItemPermissionsTaskStatus(); + taskStatus.setContentSection(section.getLabel()); + taskStatus.setStarted(LocalDateTime.now()); + taskSender.fireAsync( + new FixItemPermissionsTask( + section, + LocalDate.now(), + taskStatus, + SecurityUtils.getSecurityManager() + ) + ).handle((task, ex) -> handleTaskResult(task, ex, taskStatus)); + + taskStatus.setStatus(FixItemPermissionsStatus.RUNNING); + } + + private Object handleTaskResult( + final FixItemPermissionsTask task, + final Throwable ex, + final FixItemPermissionsTaskStatus status + ) { + if (ex == null) { + status.setStatus(FixItemPermissionsStatus.FINISHED); + } else { + status.setStatus(FixItemPermissionsStatus.ERROR); + status.setException(ex); + LOGGER.error( + "Fix Item Permissions for Content Section {} failed ", + status.getContentSection() + ); + LOGGER.error("with exception:", ex); + } + + return task; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskStatus.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskStatus.java new file mode 100644 index 000000000..b12541635 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskStatus.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; +import java.util.Objects; + +/** + * + * @author Jens Pelzetter + */ +public class FixItemPermissionsTaskStatus + implements Comparable { + + /** + * The content section for which the task was started. + */ + private String contentSection; + + /** + * When was the task started? + */ + private LocalDateTime started; + + /** + * The status of the task. + */ + private FixItemPermissionsStatus status; + + /** + * If the proces throw an exception, it is stored here. + */ + private Throwable exception; + + public String getContentSection() { + return contentSection; + } + + protected void setContentSection(final String contentSection) { + this.contentSection = contentSection; + } + + public LocalDateTime getStarted() { + return started; + } + + protected void setStarted(final LocalDateTime started) { + this.started = started; + } + + public FixItemPermissionsStatus getStatus() { + return status; + } + + protected void setStatus(final FixItemPermissionsStatus status) { + this.status = status; + } + + public Throwable getException() { + return exception; + } + + protected void setException(final Throwable exception) { + this.exception = exception; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 17 * hash + Objects.hashCode(contentSection); + hash = 17 * hash + Objects.hashCode(started); + hash = 17 * hash + Objects.hashCode(status); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof FixItemPermissionsTaskStatus) { + return false; + } + final FixItemPermissionsTaskStatus other + = (FixItemPermissionsTaskStatus) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(contentSection, other.getContentSection())) { + return false; + } + if (!Objects.equals(started, other.getStarted())) { + return false; + } + return status == other.getStatus(); + } + + public boolean canEqual(final Object obj) { + return obj instanceof FixItemPermissionsTaskStatus; + } + + @Override + public int compareTo(final FixItemPermissionsTaskStatus other) { + return Comparator + .nullsFirst( + Comparator + .comparing(FixItemPermissionsTaskStatus::getContentSection) + .thenComparing(FixItemPermissionsTaskStatus::getStarted) + ) + .compare(this, other); + } + + @Override + public String toString() { + return String.format( + "%s{" + + "contentSection = %s, " + + "started = %s, " + + "status = %s" + + "}", + super.toString(), + contentSection, + DateTimeFormatter.ISO_DATE_TIME + .withZone(ZoneId.systemDefault()) + .format(started), + Objects.toString(status) + ); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskStatusModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskStatusModel.java new file mode 100644 index 000000000..c0efd7eac --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTaskStatusModel.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("FixItemPermissionsTaskStatusModel") +public class FixItemPermissionsTaskStatusModel { + + @Inject + private ContentSectionModel sectionModel; + + @Inject + private FixItemPermissionsTaskManager taskManager; + + public boolean isTaskRunningForContentSection() { + return taskManager.isTaskActiveForContentSection( + sectionModel.getSectionName() + ); + } + + public boolean isTaskFailedForContentSection() { + return taskManager.isTaskFailedForContentSection( + sectionModel.getSectionName() + ); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTasks.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTasks.java new file mode 100644 index 000000000..0c1fae9ca --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/FixItemPermissionsTasks.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 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.librecms.ui.contentsections; + +import org.apache.shiro.util.ThreadContext; +import org.libreccm.security.Shiro; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.ObservesAsync; +import javax.inject.Inject; +import javax.transaction.Transactional; + +/** + * + * @author Jens Pelzetter + */ +@ApplicationScoped +public class FixItemPermissionsTasks { + + @Inject + private FixItemPermissions fixItemPermissions; + + @Inject + private Shiro shiro; + + @Transactional(Transactional.TxType.REQUIRED) + public void fixItemPermissions( + @ObservesAsync final FixItemPermissionsTask task + ) { + ThreadContext.bind(task.getSecurityManager()); + shiro.getSystemUser().execute(() -> executeFixItemPermissions(task)); + } + + + private void executeFixItemPermissions(final FixItemPermissionsTask task) { + fixItemPermissions.fixItemPermissions(task.getContentSection()); + } + +} diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/configuration/index.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/configuration/index.xhtml index 78355d968..fc5554d2f 100644 --- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/configuration/index.xhtml +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/configuration/index.xhtml @@ -136,6 +136,51 @@

+
+
+ +
+
+

+ + + #{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.title']} + + + + + + +

+

+ +

+ + + + #{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.running']} + + + #{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.description']} + + +

+
+
diff --git a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties index e9990b711..72d8aac9f 100644 --- a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties +++ b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties @@ -1030,3 +1030,7 @@ pages.page.details.displayname.dialog.displayname.help=The display name of the p pages.page.details.displayname.dialog.submit=Save pages.page.details.displayname.edit=Edit display name pages.page.details.dialog.displayname.label=Display name +contentsection.configuration.fixitemandassetspermissions.title=Fix permissions +contentsection.configuration.fixitemandassetspermissions.description=Repairs the permissions for items and assets +contentsection.configuration.fixitemandassetspermissions.running=Fixing permissions for item and assets... +contentsection.configuration.fixitemandassetspermissions.failed=Failed to repair permissions for items and assets. Check the log for details. diff --git a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties index 6c5312fe9..9b749cb2e 100644 --- a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties +++ b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties @@ -1031,3 +1031,7 @@ pages.page.details.displayname.dialog.displayname.help=The display name of the p pages.page.details.displayname.dialog.submit=Speichern pages.page.details.displayname.edit=Display Name bearbeiten pages.page.details.dialog.displayname.label=Display Name +contentsection.configuration.fixitemandassetspermissions.title=Berechtigungen reparieren +contentsection.configuration.fixitemandassetspermissions.description=Repariert die Berechtigungen f\u00fcr Dokumente und Assets +contentsection.configuration.fixitemandassetspermissions.running=Repariere Berechtigungen f\u00fcr Dokumente und Assets +contentsection.configuration.fixitemandassetspermissions.failed=Reparieren der Berechtigungen f\u00fcr Dokumente und Assets fehlgeschlagen. Weitere Informationen im Log. diff --git a/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java index 83c5c6fd9..7135bf078 100644 --- a/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java +++ b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java @@ -18,6 +18,8 @@ */ package org.libreccm.mvc.freemarker; +import com.arsdigita.kernel.KernelConfig; + import freemarker.cache.ClassTemplateLoader; import freemarker.cache.MultiTemplateLoader; import freemarker.cache.TemplateLoader; @@ -26,6 +28,7 @@ import freemarker.template.Configuration; import freemarker.template.TemplateExceptionHandler; import org.eclipse.krazo.engine.ViewEngineConfig; import org.eclipse.krazo.ext.freemarker.DefaultConfigurationProducer; +import org.libreccm.configuration.ConfigurationManager; import org.libreccm.theming.Themes; import javax.enterprise.inject.Produces; @@ -43,6 +46,9 @@ import javax.servlet.ServletContext; public class MvcFreemarkerConfigurationProducer extends DefaultConfigurationProducer { + @Inject + private ConfigurationManager confManager; + @Inject private Models models; @@ -51,7 +57,7 @@ public class MvcFreemarkerConfigurationProducer @Inject private Themes themes; - + @Inject private ThemeTemplateUtil themeTemplateUtil; @@ -60,15 +66,27 @@ public class MvcFreemarkerConfigurationProducer @Specializes @Override public Configuration getConfiguration() { + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class + ); + final Configuration configuration = new Configuration( Configuration.VERSION_2_3_30 ); configuration.setDefaultEncoding("UTF-8"); - configuration.setTemplateExceptionHandler( - TemplateExceptionHandler.RETHROW_HANDLER - ); - configuration.setLogTemplateExceptions(false); + if (kernelConfig.isDebugEnabled()) { + configuration.setTemplateExceptionHandler( + TemplateExceptionHandler.DEBUG_HANDLER + ); + configuration.setLogTemplateExceptions(true); + } else { + configuration.setTemplateExceptionHandler( + TemplateExceptionHandler.RETHROW_HANDLER + ); + configuration.setLogTemplateExceptions(false); + } + configuration.setWrapUncheckedExceptions(false); configuration.setLocalizedLookup(false); configuration.setTemplateLoader( diff --git a/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java b/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java index 1b7e3b7dc..969302580 100644 --- a/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java +++ b/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java @@ -57,8 +57,10 @@ public class PermissionManager implements Serializable { @SuppressWarnings("PMD.LongVariable") private static final String QUERY_PARAM_OBJECT = "object"; + @SuppressWarnings("PMD.LongVariable") private static final String QUERY_PARAM_GRANTEE = "grantee"; + @SuppressWarnings("PMD.LongVariable") private static final String QUERY_PARAM_PRIVILEGE = "privilege"; @@ -79,8 +81,12 @@ public class PermissionManager implements Serializable { * @return The permission identified by the provided {@code permissionId). */ public Optional findById(final long permissionId) { - return Optional.ofNullable(entityManager.find(Permission.class, - permissionId)); + return Optional.ofNullable( + entityManager.find( + Permission.class, + permissionId + ) + ); } /** @@ -93,7 +99,9 @@ public class PermissionManager implements Serializable { @Transactional(Transactional.TxType.REQUIRED) public List findPermissionsForRole(final Role role) { final TypedQuery query = entityManager.createNamedQuery( - "Permission.findPermissionsForRole", Permission.class); + "Permission.findPermissionsForRole", + Permission.class + ); query.setParameter("grantee", role); return query.getResultList(); @@ -110,17 +118,22 @@ public class PermissionManager implements Serializable { @Transactional(Transactional.TxType.REQUIRED) public List findPermissionsForObject(final CcmObject object) { final TypedQuery query = entityManager.createNamedQuery( - "Permission.findPermissionsForCcmObject", Permission.class); + "Permission.findPermissionsForCcmObject", + Permission.class + ); query.setParameter("object", object); return query.getResultList(); } public List findPermissionsForRoleAndObject( - final Role role, final CcmObject object) { - + final Role role, + final CcmObject object + ) { final TypedQuery query = entityManager.createNamedQuery( - "Permission.findPermissionsForRoleAndObject", Permission.class); + "Permission.findPermissionsForRoleAndObject", + Permission.class + ); query.setParameter("object", object); query.setParameter("grantee", role); @@ -148,9 +161,11 @@ public class PermissionManager implements Serializable { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public Permission grantPrivilege(final String privilege, - final Role grantee, - final CcmObject object) { + public Permission grantPrivilege( + final String privilege, + final Role grantee, + final CcmObject object + ) { if (privilege == null || privilege.isEmpty()) { throw new IllegalArgumentException( "Can't grant a permission without a privilege."); @@ -182,7 +197,13 @@ public class PermissionManager implements Serializable { entityManager.persist(permission); - grantRecursive(privilege, grantee, object, object.getClass(), object); + grantRecursive( + privilege, + grantee, + object, + object.getClass(), + object + ); return permission; } @@ -202,32 +223,44 @@ public class PermissionManager implements Serializable { * {@link #grantPrivilege(java.lang.String, org.libreccm.security.Role, org.libreccm.core.CcmObject)} * was invoked. */ - private void grantRecursive(final String privilege, - final Role grantee, - final CcmObject object, - final Class clazz, - final CcmObject inheritedFrom) { + private void grantRecursive( + final String privilege, + final Role grantee, + final CcmObject object, + final Class clazz, + final CcmObject inheritedFrom + ) { final Field[] fields = clazz.getDeclaredFields(); Arrays.stream(fields) .filter(field -> field.isAnnotationPresent( RecursivePermissions.class)) - .filter(field -> { - return checkIfPrivilegeIsRecursive( + .filter( + field -> checkIfPrivilegeIsRecursive( field.getAnnotation(RecursivePermissions.class), - privilege); - }) - .forEach(field -> { + privilege + ) + ) + .forEach( + field -> { field.setAccessible(true); - grantRecursive(privilege, grantee, field, object, inheritedFrom); + grantRecursive( + privilege, + grantee, + field, + object, + inheritedFrom + ); }); // Repeat for superclass of the current class. if (clazz.getSuperclass() != null) { - grantRecursive(privilege, - grantee, - object, - clazz.getSuperclass(), - inheritedFrom); + grantRecursive( + privilege, + grantee, + object, + clazz.getSuperclass(), + inheritedFrom + ); } } @@ -246,13 +279,14 @@ public class PermissionManager implements Serializable { */ private boolean checkIfPrivilegeIsRecursive( final RecursivePermissions annotation, - final String privilege) { - + final String privilege + ) { if (annotation.privileges() == null || annotation.privileges().length == 0) { return true; } else { - return Arrays.stream(annotation.privileges()) + return Arrays + .stream(annotation.privileges()) .anyMatch(privilege::equals); } } @@ -266,24 +300,35 @@ public class PermissionManager implements Serializable { * @param owner The object which own the provided {@code field}. * @param inheritedFrom The object from which the permission is inherited. */ - private void grantRecursive(final String privilege, - final Role grantee, - final Field field, - final CcmObject owner, - final CcmObject inheritedFrom) { - + private void grantRecursive( + final String privilege, + final Role grantee, + final Field field, + final CcmObject owner, + final CcmObject inheritedFrom + ) { final CcmObject ownerObject = ccmObjectRepo .findObjectById(owner.getObjectId()) - .orElseThrow(() -> new IllegalArgumentException(String.format( - "No CcmObject with ID %d in the database. " - + "Where did that ID come from?", - owner.getObjectId()))); + .orElseThrow( + () -> new IllegalArgumentException( + String.format( + "No CcmObject with ID %d in the database. " + + "Where did that ID come from?", + owner.getObjectId() + ) + ) + ); final CcmObject inheritedFromObject = ccmObjectRepo .findObjectById(inheritedFrom.getObjectId()) - .orElseThrow(() -> new IllegalArgumentException(String.format( - "No CcmObject with ID %d in the database. " - + "Where did that ID come from?", - inheritedFrom.getObjectId()))); + .orElseThrow( + () -> new IllegalArgumentException( + String.format( + "No CcmObject with ID %d in the database. " + + "Where did that ID come from?", + inheritedFrom.getObjectId() + ) + ) + ); final Object value; try { @@ -303,10 +348,14 @@ public class PermissionManager implements Serializable { collection.stream() .filter(obj -> obj instanceof CcmObject) .map(obj -> (CcmObject) obj) - .forEach(obj -> grantInherited(privilege, - grantee, - obj, - inheritedFromObject)); + .forEach( + obj -> grantInherited( + privilege, + grantee, + obj, + inheritedFromObject + ) + ); // Relations between two CcmObjects with attributes or n:m relations // use an object to represent the relation. The object must implement // the Relation interface. For each Relation object in the collection @@ -316,33 +365,44 @@ public class PermissionManager implements Serializable { .map(obj -> (Relation) obj) .filter(relation -> relation.getRelatedObject() != null) .map(relation -> relation.getRelatedObject()) - .forEach(obj -> grantInherited(privilege, - grantee, - obj, - inheritedFromObject)); + .forEach( + obj -> grantInherited( + privilege, + grantee, + obj, + inheritedFromObject + ) + ); } else if (CcmObject.class.isAssignableFrom(field.getType())) { // If the provided object is a CcmObject create an inherited // permission for this object. - grantInherited(privilege, - grantee, - (CcmObject) value, - inheritedFromObject); + grantInherited( + privilege, + grantee, + (CcmObject) value, + inheritedFromObject + ); } else if (Relation.class.isAssignableFrom(field.getType())) { // If the provided field is a Relation object created an inherited // permission on the related object. final Relation relation = (Relation) value; if (relation.getRelatedObject() != null) { - grantInherited(privilege, - grantee, - relation.getRelatedObject(), - inheritedFromObject); + grantInherited( + privilege, + grantee, + relation.getRelatedObject(), + inheritedFromObject + ); } } else { - throw new IllegalArgumentException(String.format( - "Found a field annotated with \"%s\" but the field is not a " + throw new IllegalArgumentException( + String.format( + "Found a field annotated with \"%s\" but the field is not a " + "collection nor a CcmObject nore a Relation object. This " - + "is not supported.", - RecursivePermissions.class)); + + "is not supported.", + RecursivePermissions.class + ) + ); } } @@ -355,11 +415,12 @@ public class PermissionManager implements Serializable { * granted. * @param inheritedFrom The object from which the permission is inherited. */ - private void grantInherited(final String privilege, - final Role grantee, - final CcmObject object, - final CcmObject inheritedFrom) { - + private void grantInherited( + final String privilege, + final Role grantee, + final CcmObject object, + final CcmObject inheritedFrom + ) { if (!existsPermission(privilege, grantee, object)) { final Permission permission = new Permission(); permission.setGrantee(grantee); @@ -371,11 +432,13 @@ public class PermissionManager implements Serializable { entityManager.persist(permission); - grantRecursive(privilege, - grantee, - object, - object.getClass(), - inheritedFrom); + grantRecursive( + privilege, + grantee, + object, + object.getClass(), + inheritedFrom + ); } } @@ -389,16 +452,20 @@ public class PermissionManager implements Serializable { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public void grantPrivilege(final String privilege, - final Role grantee) { + public void grantPrivilege( + final String privilege, + final Role grantee + ) { if (privilege == null || privilege.isEmpty()) { throw new IllegalArgumentException( - "Can't grant a permission without a privilege."); + "Can't grant a permission without a privilege." + ); } if (grantee == null) { throw new IllegalArgumentException( - "Can't grant a permission to grantee null."); + "Can't grant a permission to grantee null." + ); } if (!existsPermission(privilege, grantee)) { @@ -423,42 +490,52 @@ public class PermissionManager implements Serializable { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public void revokePrivilege(final String privilege, - final Role grantee, - final CcmObject object) { + public void revokePrivilege( + final String privilege, + final Role grantee, + final CcmObject object + ) { if (privilege == null || privilege.isEmpty()) { throw new IllegalArgumentException( - "Can't revoke a permission without a privilege."); + "Can't revoke a permission without a privilege." + ); } if (grantee == null) { throw new IllegalArgumentException( - "Can't revoke a permission from grantee null."); + "Can't revoke a permission from grantee null." + ); } if (object == null) { throw new IllegalArgumentException( - "Can't revoke a permission from object NULL."); + "Can't revoke a permission from object NULL." + ); } - LOGGER.debug("Revoking permission granting privilege \"{}\" " - + "on object \"{}\" to role \"{}\"...", - privilege, - grantee.getName(), - object.getUuid()); + LOGGER.debug( + "Revoking permission granting privilege \"{}\" " + + "on object \"{}\" to role \"{}\"...", + privilege, + grantee.getName(), + object.getUuid() + ); if (existsPermission(privilege, grantee, object) || existsInheritedPermission(privilege, grantee, object)) { - LOGGER.debug("There is a permission for the provided parameters, " - + "revoking it..."); + LOGGER.debug( + "There is a permission for the provided parameters, " + + "revoking it..." + ); final Query deleteQuery = entityManager.createQuery( "DELETE FROM Permission p " + "WHERE p.grantedPrivilege = :privilege " + "AND p.grantee = :grantee " - + "AND p.object = :object"); + + "AND p.object = :object" + ); deleteQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege); deleteQuery.setParameter(QUERY_PARAM_GRANTEE, grantee); deleteQuery.setParameter(QUERY_PARAM_OBJECT, object); @@ -470,18 +547,21 @@ public class PermissionManager implements Serializable { + "WHERE p.grantedPrivilege = :privilege " + "AND p.grantee = :grantee " + "AND p.inheritedFrom = :object " - + "AND p.inherited = true"); + + "AND p.inherited = true" + ); deleteInheritedQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege); deleteInheritedQuery.setParameter(QUERY_PARAM_GRANTEE, grantee); deleteInheritedQuery.setParameter("object", object); final int deletedInherited = deleteInheritedQuery.executeUpdate(); LOGGER.debug("{} inherited permissions deleted.", deletedInherited); } else { - LOGGER.warn("No permission granting privilege \"{}\" " - + "on object \"{}\" to role \"{}\". Ignoring.", - privilege, - grantee.getName(), - object.getUuid()); + LOGGER.warn( + "No permission granting privilege \"{}\" " + + "on object \"{}\" to role \"{}\". Ignoring.", + privilege, + grantee.getName(), + object.getUuid() + ); } } @@ -495,16 +575,20 @@ public class PermissionManager implements Serializable { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public void revokePrivilege(final String privilege, - final Role grantee) { + public void revokePrivilege( + final String privilege, + final Role grantee + ) { if (privilege == null || privilege.isEmpty()) { throw new IllegalArgumentException( - "Can't revoke a permission without a privilege."); + "Can't revoke a permission without a privilege." + ); } if (grantee == null) { throw new IllegalArgumentException( - "Can't revoke a permission from grantee null."); + "Can't revoke a permission from grantee null." + ); } if (existsPermission(privilege, grantee)) { @@ -512,7 +596,8 @@ public class PermissionManager implements Serializable { "DELETE FROM Permission p " + "WHERE p.grantedPrivilege = :privilege " + "AND p.grantee = :grantee " - + "AND p.object IS NULL"); + + "AND p.object IS NULL" + ); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); query.setParameter(QUERY_PARAM_GRANTEE, grantee); query.executeUpdate(); @@ -532,8 +617,10 @@ public class PermissionManager implements Serializable { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public void copyPermissions(final CcmObject source, - final CcmObject target) { + public void copyPermissions( + final CcmObject source, + final CcmObject target + ) { copyPermissions(source, target, false); } @@ -552,21 +639,27 @@ public class PermissionManager implements Serializable { @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) - public void copyPermissions(final CcmObject source, - final CcmObject target, - final boolean inherited) { + public void copyPermissions( + final CcmObject source, + final CcmObject target, + final boolean inherited + ) { if (source == null) { throw new IllegalArgumentException( - "Can't copy permissions from source NULL."); + "Can't copy permissions from source NULL." + ); } if (target == null) { throw new IllegalArgumentException( - "Can't copy permissions to target NULL."); + "Can't copy permissions to target NULL." + ); } final TypedQuery query = entityManager.createNamedQuery( - "Permission.findPermissionsForCcmObject", Permission.class); + "Permission.findPermissionsForCcmObject", + Permission.class + ); query.setParameter(QUERY_PARAM_OBJECT, source); final List result = query.getResultList(); @@ -574,7 +667,11 @@ public class PermissionManager implements Serializable { final Permission granted = grantPrivilege( permission.getGrantedPrivilege(), permission.getGrantee(), - target); + target + ); + if (granted == null) { + continue; + } granted.setInherited(inherited); if (inherited) { granted.setInheritedFrom(source); @@ -598,12 +695,17 @@ public class PermissionManager implements Serializable { * @return A list with all privileges defined by the provided class. */ public List listDefiniedPrivileges(final Class clazz) { - return Arrays.stream(clazz.getDeclaredFields()) + return Arrays + .stream(clazz.getDeclaredFields()) .filter(field -> field.getType().isAssignableFrom(String.class)) - .filter(field -> Modifier.isStatic(field.getModifiers()) - && Modifier.isFinal(field.getModifiers())) - .filter(field -> field.getName().startsWith("PRIVILEGE_") - || clazz.getSimpleName().endsWith("Privileges")) + .filter( + field -> Modifier.isStatic(field.getModifiers()) + && Modifier.isFinal(field.getModifiers()) + ) + .filter( + field -> field.getName().startsWith("PRIVILEGE_") + || clazz.getSimpleName().endsWith("Privileges") + ) .map(field -> getPrivilegeString(field)) .sorted() .collect(Collectors.toList()); @@ -629,11 +731,15 @@ public class PermissionManager implements Serializable { * @return {@code true} if there is a matching permission, {@code false} if * not. */ - private boolean existsPermission(final String privilege, - final Role grantee, - final CcmObject object) { + private boolean existsPermission( + final String privilege, + final Role grantee, + final CcmObject object + ) { final TypedQuery query = entityManager.createNamedQuery( - "Permission.existsDirectForPrivilegeRoleObject", Long.class); + "Permission.existsDirectForPrivilegeRoleObject", + Long.class + ); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); query.setParameter(QUERY_PARAM_GRANTEE, grantee); query.setParameter(QUERY_PARAM_OBJECT, object); @@ -641,11 +747,15 @@ public class PermissionManager implements Serializable { return query.getSingleResult() > 0; } - private boolean existsInheritedPermission(final String privilege, - final Role grantee, - final CcmObject object) { + private boolean existsInheritedPermission( + final String privilege, + final Role grantee, + final CcmObject object + ) { final TypedQuery query = entityManager.createNamedQuery( - "Permission.existsInheritedForPrivilegeRoleObject", Long.class); + "Permission.existsInheritedForPrivilegeRoleObject", + Long.class + ); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); query.setParameter(QUERY_PARAM_GRANTEE, grantee); query.setParameter(QUERY_PARAM_OBJECT, object); @@ -663,10 +773,14 @@ public class PermissionManager implements Serializable { * @return {@code true} if there is a matching permission, {@code false} if * not. */ - private boolean existsPermission(final String privilege, - final Role grantee) { + private boolean existsPermission( + final String privilege, + final Role grantee + ) { final TypedQuery query = entityManager.createNamedQuery( - "Permission.existsForPrivilegeAndRole", Long.class); + "Permission.existsForPrivilegeAndRole", + Long.class + ); query.setParameter(QUERY_PARAM_PRIVILEGE, privilege); query.setParameter(QUERY_PARAM_GRANTEE, grantee); diff --git a/ccm-core/src/main/java/org/libreccm/theming/freemarker/FreemarkerConfigurationProvider.java b/ccm-core/src/main/java/org/libreccm/theming/freemarker/FreemarkerConfigurationProvider.java index 000918402..89a7d367a 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/freemarker/FreemarkerConfigurationProvider.java +++ b/ccm-core/src/main/java/org/libreccm/theming/freemarker/FreemarkerConfigurationProvider.java @@ -57,32 +57,36 @@ class FreemarkerConfigurationProvider { private final Map configurations = new HashMap<>(); protected Configuration getConfiguration(final ThemeInfo forTheme) { - if (configurations.containsKey(forTheme)) { - return configurations.get(forTheme); } else { - final Configuration configuration = new Configuration( - Configuration.VERSION_2_3_27); + Configuration.VERSION_2_3_27 + ); configuration.setDefaultEncoding("UTF-8"); configuration .setTemplateExceptionHandler( - TemplateExceptionHandler.RETHROW_HANDLER); - configuration.setLogTemplateExceptions(false); + TemplateExceptionHandler.DEBUG_HANDLER + ); + configuration.setLogTemplateExceptions(true); configuration.setWrapUncheckedExceptions(false); configuration.setLocalizedLookup(false); configuration.setTemplateLoader( - new MultiTemplateLoader(new TemplateLoader[]{ - // For for files from themes - new CcmTemplateLoader(forTheme), - // Loader for MacroLibs provided by CCM modules - new WebappTemplateLoader( - servletContext, "/themes/freemarker" - ), - new ClassTemplateLoader(getClass(), "/themes/freemarker") - }) + new MultiTemplateLoader( + new TemplateLoader[]{ + // For for files from themes + new CcmTemplateLoader(forTheme), + // Loader for MacroLibs provided by CCM modules + new WebappTemplateLoader( + servletContext, "/themes/freemarker" + ), + new ClassTemplateLoader( + getClass(), + "/themes/freemarker" + ) + } + ) ); configurations.put(forTheme, configuration); @@ -103,7 +107,8 @@ class FreemarkerConfigurationProvider { public Object findTemplateSource(final String name) throws IOException { final Optional source = themes.getFileFromTheme( - fromTheme, name); + fromTheme, name + ); if (source.isPresent()) { return source.get(); } else { @@ -118,9 +123,10 @@ class FreemarkerConfigurationProvider { } @Override - public Reader getReader(final Object templateSource, - final String encoding) throws IOException { - + public Reader getReader( + final Object templateSource, + final String encoding + ) throws IOException { final InputStream inputStream = (InputStream) templateSource; return new InputStreamReader(inputStream, encoding); } @@ -128,7 +134,6 @@ class FreemarkerConfigurationProvider { @Override public void closeTemplateSource(final Object templateSource) throws IOException { - final InputStream inputStream = (InputStream) templateSource; inputStream.close(); } diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java index ec51a8382..498378362 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/imexport/ImportExportTaskManager.java @@ -217,7 +217,9 @@ public class ImportExportTaskManager { * @see CompletionStage#handle(java.util.function.BiFunction) */ private Object handleExportTaskResult( - final ExportTask task, final Throwable ex, final ExportTaskStatus status + final ExportTask task, + final Throwable ex, + final ExportTaskStatus status ) { if (ex == null) { status.setStatus(ImExportTaskStatus.FINISHED);