Skip to content

Commit

Permalink
devonfw-forge#28: various improvements on authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
hohwille committed Sep 3, 2019
1 parent 5f7fd50 commit 2651134
Show file tree
Hide file tree
Showing 15 changed files with 363 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.devonfw.keywi.general.common.api;

import javax.inject.Named;

import net.sf.mmm.util.nls.api.NlsBundle;
import net.sf.mmm.util.nls.api.NlsBundleMessage;
import net.sf.mmm.util.nls.api.NlsMessage;

/**
* This is the {@link NlsBundle} for this application.
*/
public interface NlsBundleApplicationRoot extends NlsBundle {

/**
* @return error message if the user tried to modify the business key.
* @see com.devonfw.keywi.keymanagement.common.api.KeyObject#getKey()
*
* @param entity the {@link String} describing the entity that could not be modified.
* @param key the new business key the user tried to change to.
*/
@NlsBundleMessage("You can not change the business key of entity {entity} to {key}! If you really need to change the business key, create a new entity and delete the old one.")
NlsMessage errorKeyImmutable(@Named("entity") String entity, @Named("key") String key);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.devonfw.keywi.general.common.api.exception;

import net.sf.mmm.util.exception.api.NlsRuntimeException;
import net.sf.mmm.util.nls.api.NlsMessage;

import com.devonfw.keywi.general.common.api.NlsBundleApplicationRoot;

/**
* Abstract base class for business exceptions of this application.
*/
public abstract class ApplicationBusinessException extends NlsRuntimeException {

private static final long serialVersionUID = 1L;

/**
* The constructor.
*
* @param cause is the {@link #getCause() cause} of this exception. May be {@code null}.
* @param message the {@link #getNlsMessage() message} describing the problem briefly.
* @see #createBundle()
*/
public ApplicationBusinessException(Throwable cause, NlsMessage message) {

super(cause, message);
}

/**
* @return the {@link NlsBundleApplicationRoot} to create the actual {@link NlsMessage}.
*/
protected static NlsBundleApplicationRoot createBundle() {

return createBundle(NlsBundleApplicationRoot.class);
}

@Override
public boolean isForUser() {

return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
@Named
public class ApplicationAccessControlConfig extends AccessControlConfig {

/** The ID of this app. */
public static final String APP_ID = "keywi";

/** The prefix used by permissions and groups of this app. */
public static final String PREFIX = APP_ID + ".";

/** @see com.devonfw.keywi.keymanagement.logic.api.usecase.UcFindKeyItem */
Expand All @@ -23,7 +25,9 @@ public class ApplicationAccessControlConfig extends AccessControlConfig {
*/
public static final String PERMISSION_SAVE_KEY_ITEM = PREFIX + "SaveKeyItem";

/** @see com.devonfw.keywi.keymanagement.logic.api.usecase.UcManageKeyItem#deleteKeyItem(long) */
/**
* @see com.devonfw.keywi.keymanagement.logic.api.usecase.UcManageKeyItem#deleteKeyItem(com.devonfw.module.basic.common.api.reference.IdRef)
*/
public static final String PERMISSION_DELETE_KEY_ITEM = PREFIX + "DeleteKeyItem";

/** @see com.devonfw.keywi.keymanagement.logic.api.usecase.UcFindKeyList */
Expand All @@ -34,11 +38,18 @@ public class ApplicationAccessControlConfig extends AccessControlConfig {
*/
public static final String PERMISSION_SAVE_KEY_LIST = PREFIX + "SaveKeyList";

/** @see com.devonfw.keywi.keymanagement.logic.api.usecase.UcManageKeyList#deleteKeyList(long) */
/**
* @see com.devonfw.keywi.keymanagement.logic.api.usecase.UcManageKeyList#deleteKeyList(com.devonfw.module.basic.common.api.reference.IdRef)
*/
public static final String PERMISSION_DELETE_KEY_LIST = PREFIX + "DeleteKeyList";

/** Group for overall read-access in the app. */
public static final String GROUP_READ_MASTER_DATA = PREFIX + "ReadMasterData";

/** Group for manager of key lists entries. */
public static final String GROUP_MANAGER = PREFIX + "Manager";

/** Group for administration permission in the app. */
public static final String GROUP_ADMIN = PREFIX + "Admin";

/**
Expand All @@ -47,10 +58,10 @@ public class ApplicationAccessControlConfig extends AccessControlConfig {
public ApplicationAccessControlConfig() {

super();
AccessControlGroup readMasterData =
group(GROUP_READ_MASTER_DATA, PERMISSION_FIND_KEY_LIST, PERMISSION_FIND_KEY_ITEM);
group(GROUP_ADMIN, readMasterData, PERMISSION_SAVE_KEY_ITEM, PERMISSION_SAVE_KEY_LIST, PERMISSION_DELETE_KEY_ITEM,
PERMISSION_DELETE_KEY_LIST);
AccessControlGroup readMasterData = group(GROUP_READ_MASTER_DATA, PERMISSION_FIND_KEY_LIST,
PERMISSION_FIND_KEY_ITEM);
AccessControlGroup manager = group(GROUP_MANAGER, readMasterData, PERMISSION_SAVE_KEY_ITEM);
group(GROUP_ADMIN, manager, PERMISSION_SAVE_KEY_LIST, PERMISSION_DELETE_KEY_ITEM, PERMISSION_DELETE_KEY_LIST);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import com.devonfw.keywi.general.common.base.AbstractBeanMapperSupport;
import com.devonfw.module.basic.common.api.entity.GenericEntity;
Expand Down Expand Up @@ -98,4 +105,48 @@ protected <T extends Serializable, E extends PersistenceEntity<?>> Page<T> mapPa
return new PageImpl<>(etoList, page.getPageable(), page.getTotalElements());
}

/**
* @param permissions required permission(s)
* @throws AccessDeniedException in case there is no current user logged-in or he is missing one of the given
* {@code permissions}.
*/
protected void requirePermissions(String... permissions) throws AccessDeniedException {

Set<String> userPermissions = getUserPermissions();
for (String permission : permissions) {
boolean authorized = userPermissions.contains(permission);
if (!authorized) {
throw new AccessDeniedException("Permission required: " + permission);
}
}
}

private Set<String> getUserPermissions() {

Set<String> userPermissions = new HashSet<>();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
for (GrantedAuthority authority : authentication.getAuthorities()) {
userPermissions.add(authority.getAuthority());
}
}
return userPermissions;
}

/**
* @param permissions die erforderliche(n) Berechtigung(en).
* @throws AccessDeniedException falls der aktuell angemeldete Nutzer keine von der erforderlichen Berechtigungen
* besitzt.
*/
protected void requireAnyPermission(String... permissions) throws AccessDeniedException {

Set<String> userPermissions = getUserPermissions();
for (String permission : permissions) {
boolean authorized = userPermissions.contains(permission);
if (authorized) {
return;
}
}
throw new AccessDeniedException("Permission required out of " + Arrays.toString(permissions));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.devonfw.keywi.keymanagement.common.api.exception;

import com.devonfw.keywi.general.common.api.exception.ApplicationBusinessException;
import com.devonfw.keywi.keymanagement.common.api.KeyObject;

/**
* Thrown if the user tried to modify the business key of an existing entity.
*/
public class KeyModificationException extends ApplicationBusinessException {

private static final long serialVersionUID = 1L;

/**
* The constructor.
*
* @param entity the {@link String} describing the entity that could not be modified. Should at least contain the
* original business key.
* @param key the new business key the user tried to save.
*/
public KeyModificationException(KeyObject entity, String key) {

this(entity, key, null);
}

/**
* The constructor.
*
* @param entity the {@link String} describing the entity that could not be modified. Should at least contain the
* original business key.
* @param key the new business key the user tried to save.
* @param cause is the {@link #getCause() cause} of this exception. May be {@code null}.
*/
public KeyModificationException(KeyObject entity, String key, Throwable cause) {

super(cause, createBundle().errorKeyImmutable(toString(entity), key));
}

private static String toString(KeyObject entity) {

StringBuilder sb = new StringBuilder(entity.getClass().getSimpleName());
sb.append("[id=");
sb.append(entity.getId());
sb.append(",key=");
sb.append(entity.getKey());
sb.append(']');
return sb.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@

import javax.inject.Inject;

import com.devonfw.keywi.general.logic.base.AbstractUc;
import com.devonfw.keywi.keymanagement.dataaccess.api.repo.KeyItemRepository;

/**
* Abstract use case for KeyItems, which provides access to the commonly necessary data access objects.
* Abstract use case for {@link com.devonfw.keywi.keymanagement.common.api.KeyItem}s providing database access.
*/
public class AbstractKeyItemUc extends AbstractUc {
public class AbstractKeyItemUc extends AbstractKeyObjectUc {

/** @see #getKeyItemRepository() */
@Inject
private KeyItemRepository keyItemRepository;

/**
* Returns the field 'keyItemRepository'.
*
* @return the {@link KeyItemRepository} instance.
*/
public KeyItemRepository getKeyItemRepository() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@

import javax.inject.Inject;

import com.devonfw.keywi.general.logic.base.AbstractUc;
import com.devonfw.keywi.keymanagement.dataaccess.api.repo.KeyListRepository;

/**
* Abstract use case for KeyLists, which provides access to the commonly necessary data access objects.
* Abstract use case for {@link com.devonfw.keywi.keymanagement.common.api.KeyList}s providing database access.
*/
public class AbstractKeyListUc extends AbstractUc {
public class AbstractKeyListUc extends AbstractKeyObjectUc {

/** @see #getKeyListRepository() */
@Inject
private KeyListRepository keyListRepository;

/**
* Returns the field 'keyListRepository'.
*
* @return the {@link KeyListRepository} instance.
*/
public KeyListRepository getKeyListRepository() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.devonfw.keywi.keymanagement.logic.base.usecase;

import java.util.Objects;

import com.devonfw.keywi.general.logic.base.AbstractUc;
import com.devonfw.keywi.keymanagement.common.api.KeyObject;
import com.devonfw.keywi.keymanagement.common.api.exception.KeyModificationException;
import com.devonfw.keywi.keymanagement.dataaccess.api.KeyObjectEntity;
import com.devonfw.keywi.keymanagement.logic.api.to.KeyObjectEto;

/**
* Abstract use case for {@link KeyObject}s.
*/
public class AbstractKeyObjectUc extends AbstractUc {

/**
* Verifies that the {@link com.devonfw.keywi.keymanagement.common.api.KeyObject#getKey() business key} of both given
* {@link com.devonfw.keywi.keymanagement.common.api.KeyObject}s are equal.
*
* @param keyObjectToSave the {@link KeyObjectEto} to save.
* @param keyObjectFromDb the {@link KeyObjectEntity} loaded from the Database that should be modified.
*/
protected void verifyKeyNotModified(KeyObjectEto keyObjectToSave, KeyObjectEntity keyObjectFromDb) {

String keyToSave = keyObjectToSave.getKey();
if (!Objects.equals(keyToSave, keyObjectFromDb.getKey())) {
throw new KeyModificationException(keyObjectFromDb, keyToSave);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public KeyItemEto saveKeyItem(KeyItemEto keyItem) {
Objects.requireNonNull(keyItem, "keyItem");

KeyItemEntity keyItemEntity = getBeanMapper().map(keyItem, KeyItemEntity.class);
Long id = keyItem.getId();
if (id != null) {
KeyItemEntity keyItemFromDb = getKeyItemRepository().find(id);
verifyKeyNotModified(keyItem, keyItemFromDb);
}

// initialize, validate keyItemEntity here if necessary
KeyItemEntity resultEntity = getKeyItemRepository().save(keyItemEntity);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.devonfw.keywi.keymanagement.logic.impl.usecase;

import java.util.Objects;
import java.util.Optional;

import javax.annotation.security.RolesAllowed;
import javax.inject.Named;
Expand Down Expand Up @@ -36,9 +37,18 @@ public boolean deleteKeyList(IdRef<KeyList> id) {
if (id == null) {
return false;
}
getKeyListRepository().deleteById(id.getId());
LOG.debug("The keyList with id '{}' has been deleted.", id);
return true;
Optional<KeyListEntity> keyList = getKeyListRepository().findById(id.getId());
if (keyList.isPresent()) {
KeyListEntity entity = keyList.get();
String permission = entity.getPermission();
if ((permission != null) && !permission.isEmpty()) {
requireAnyPermission(permission, ApplicationAccessControlConfig.GROUP_ADMIN);
}
getKeyListRepository().delete(entity);
LOG.debug("The keyList with id '{}' has been deleted.", id);
return true;
}
return false;
}

@Override
Expand All @@ -49,7 +59,18 @@ public KeyListEto saveKeyList(KeyListEto keyList) {

KeyListEntity keyListEntity = getBeanMapper().map(keyList, KeyListEntity.class);

// initialize, validate keyListEntity here if necessary
KeyList entity = keyList;
Long id = keyList.getId();
if (id != null) {
KeyListEntity keyListFromDb = getKeyListRepository().find(id);
verifyKeyNotModified(keyList, keyListFromDb);
entity = keyListFromDb;
}
String permission = entity.getPermission();
if ((permission != null) && !permission.isEmpty()) {
requireAnyPermission(permission, ApplicationAccessControlConfig.GROUP_ADMIN);
}

KeyListEntity resultEntity = getKeyListRepository().save(keyListEntity);
LOG.debug("KeyList with id '{}' has been created.", resultEntity.getId());
return getBeanMapper().map(resultEntity, KeyListEto.class);
Expand Down
Loading

0 comments on commit 2651134

Please sign in to comment.