Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Sofia Papacharalampous 2024-04-16 11:37:23 +03:00
commit dcbaf92128
34 changed files with 337 additions and 96 deletions

View File

@ -67,7 +67,7 @@ public class TenantScope {
this.tenant.set(tenant);
this.tenantCode.set(tenantCode);
if (this.tenant.get() != null && !this.isDefaultTenant()) {
if (this.tenant.get() != null || this.isDefaultTenant()) {
if(!this.isDefaultTenant()) {
entityManager
.unwrap(Session.class)
@ -84,7 +84,7 @@ public class TenantScope {
public void removeTempTenant(EntityManager entityManager) {
this.tenant.set(this.initialTenant.get());
this.tenantCode.set(this.initialTenantCode.get());
if (this.initialTenant.get() != null && !this.isDefaultTenant()) {
if (this.initialTenant.get() != null || this.isDefaultTenant()) {
if(!this.isDefaultTenant()) {
entityManager
.unwrap(Session.class)

View File

@ -39,9 +39,13 @@ public class User {
public static final String _contacts = "contacts";
private List<UserRole> roles;
private List<UserRole> globalRoles;
public static final String _roles = "roles";
public static final String _globalRoles = "globalRoles";
private List<UserRole> tenantRoles;
public static final String _tenantRoles = "tenantRoles";
private List<UserCredential> credentials;
@ -114,14 +118,6 @@ public class User {
this.contacts = contacts;
}
public List<UserRole> getRoles() {
return roles;
}
public void setRoles(List<UserRole> roles) {
this.roles = roles;
}
public List<UserCredential> getCredentials() {
return credentials;
}
@ -137,4 +133,20 @@ public class User {
public void setTenantUsers(List<TenantUser> tenantUsers) {
this.tenantUsers = tenantUsers;
}
public List<UserRole> getTenantRoles() {
return tenantRoles;
}
public void setTenantRoles(List<UserRole> tenantRoles) {
this.tenantRoles = tenantRoles;
}
public List<UserRole> getGlobalRoles() {
return globalRoles;
}
public void setGlobalRoles(List<UserRole> globalRoles) {
this.globalRoles = globalRoles;
}
}

View File

@ -1,18 +1,21 @@
package eu.eudat.model.builder;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.AuthorizationProperties;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.types.user.AdditionalInfoEntity;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.data.UserEntity;
import eu.eudat.model.*;
import eu.eudat.query.TenantUserQuery;
import eu.eudat.query.UserContactInfoQuery;
import eu.eudat.query.UserCredentialQuery;
import eu.eudat.query.UserRoleQuery;
import eu.eudat.query.*;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
@ -20,9 +23,12 @@ import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.*;
import java.util.stream.Collectors;
@ -34,17 +40,26 @@ public class UserBuilder extends BaseBuilder<User, UserEntity> {
private final BuilderFactory builderFactory;
private final JsonHandlingService jsonHandlingService;
private final AuthorizationProperties authorizationProperties;
private final TenantEntityManager tenantEntityManager;
private final TenantScope tenantScope;
private final MessageSource messageSource;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired
public UserBuilder(ConventionService conventionService,
QueryFactory queryFactory,
BuilderFactory builderFactory, JsonHandlingService jsonHandlingService) {
BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, AuthorizationProperties authorizationProperties, TenantEntityManager tenantEntityManager, TenantScope tenantScope, MessageSource messageSource) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(UserBuilder.class)));
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.jsonHandlingService = jsonHandlingService;
this.authorizationProperties = authorizationProperties;
this.tenantEntityManager = tenantEntityManager;
this.tenantScope = tenantScope;
this.messageSource = messageSource;
}
public UserBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -64,8 +79,11 @@ public class UserBuilder extends BaseBuilder<User, UserEntity> {
FieldSet contactsFields = fields.extractPrefixed(this.asPrefix(User._contacts));
Map<UUID, List<UserContactInfo>> contactsMap = this.collectUserContactInfos(contactsFields, data);
FieldSet rolesFields = fields.extractPrefixed(this.asPrefix(User._roles));
Map<UUID, List<UserRole>> rolesMap = this.collectUserRoles(rolesFields, data);
FieldSet globalRolesFields = fields.extractPrefixed(this.asPrefix(User._globalRoles));
Map<UUID, List<UserRole>> globalRolesMap = this.collectUserGlobalRoles(globalRolesFields, data);
FieldSet tenantRolesFields = fields.extractPrefixed(this.asPrefix(User._tenantRoles));
Map<UUID, List<UserRole>> tenantRolesMap = this.collectUserTenantRoles(tenantRolesFields, data);
FieldSet credentialsFields = fields.extractPrefixed(this.asPrefix(User._credentials));
Map<UUID, List<UserCredential>> credentialsMap = this.collectUserCredentials(credentialsFields, data);
@ -83,7 +101,8 @@ public class UserBuilder extends BaseBuilder<User, UserEntity> {
if (fields.hasField(this.asIndexer(User._isActive))) m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(User._hash))) m.setHash(this.hashValue(d.getUpdatedAt()));
if (contactsMap != null && !contactsFields.isEmpty() && contactsMap.containsKey(d.getId())) m.setContacts(contactsMap.get(d.getId()));
if (rolesMap != null && !rolesFields.isEmpty() && rolesMap.containsKey(d.getId())) m.setRoles(rolesMap.get(d.getId()));
if (globalRolesMap != null && !globalRolesFields.isEmpty() && globalRolesMap.containsKey(d.getId())) m.setGlobalRoles(globalRolesMap.get(d.getId()));
if (tenantRolesMap != null && !tenantRolesFields.isEmpty() && tenantRolesMap.containsKey(d.getId())) m.setTenantRoles(tenantRolesMap.get(d.getId()));
if (credentialsMap != null && !credentialsFields.isEmpty() && credentialsMap.containsKey(d.getId())) m.setCredentials(credentialsMap.get(d.getId()));
if (!additionalInfoFields.isEmpty() && d.getAdditionalInfo() != null){
AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, d.getAdditionalInfo());
@ -115,13 +134,42 @@ public class UserBuilder extends BaseBuilder<User, UserEntity> {
return itemMap;
}
private Map<UUID, List<UserRole>> collectUserRoles(FieldSet fields, List<UserEntity> data) throws MyApplicationException {
private Map<UUID, List<UserRole>> collectUserGlobalRoles(FieldSet fields, List<UserEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", UserRole.class.getSimpleName());
Map<UUID, List<UserRole>> itemMap;
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(UserRole._user, User._id));
UserRoleQuery query = this.queryFactory.query(UserRoleQuery.class).authorize(this.authorize).userIds(data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList()));
UserRoleQuery query = this.queryFactory.query(UserRoleQuery.class).authorize(this.authorize).tenantIsSet(false).roles(this.authorizationProperties.getAllowedGlobalRoles()).userIds(data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList()));
itemMap = this.builderFactory.builder(UserRoleBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getUser().getId());
if (!fields.hasField(this.asIndexer(UserRole._user, User._id))) {
itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getUser() != null).peek(x -> {
x.getUser().setId(null);
});
}
return itemMap;
}
private Map<UUID, List<UserRole>> collectUserTenantRoles(FieldSet fields, List<UserEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", UserRole.class.getSimpleName());
Map<UUID, List<UserRole>> itemMap;
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(UserRole._user, User._id));
if (!tenantScope.isSet()) throw new MyForbiddenException("tenant scope required");
UserRoleQuery query = this.queryFactory.query(UserRoleQuery.class).authorize(this.authorize).roles(this.authorizationProperties.getAllowedTenantRoles()).userIds(data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList()));
if (tenantScope.isDefaultTenant()) query.tenantIsSet(false);
else {
try {
query.tenantIsSet(true).tenantIds(this.tenantScope.getTenant());
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
}
itemMap = this.builderFactory.builder(UserRoleBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getUser().getId());
if (!fields.hasField(this.asIndexer(UserRole._user, User._id))) {

View File

@ -39,8 +39,11 @@ public class UserCensor extends BaseCensor {
return;
this.authService.authorizeAtLeastOneForce(userId != null ? List.of(new OwnedResource(userId)) : null, Permission.BrowseUser, Permission.DeferredAffiliation);
FieldSet rolesFields = fields.extractPrefixed(this.asIndexerPrefix(User._roles));
this.censorFactory.censor(UserRoleCensor.class).censor(rolesFields, userId);
FieldSet globalRolesFields = fields.extractPrefixed(this.asIndexerPrefix(User._globalRoles));
this.censorFactory.censor(UserRoleCensor.class).censor(globalRolesFields, userId);
FieldSet tenantRolesFields = fields.extractPrefixed(this.asIndexerPrefix(User._tenantRoles));
this.censorFactory.censor(UserRoleCensor.class).censor(tenantRolesFields, userId);
FieldSet contactsFields = fields.extractPrefixed(this.asIndexerPrefix(User._contacts));
this.censorFactory.censor(UserContactInfoCensor.class).censor(contactsFields, userId);

View File

@ -4,10 +4,7 @@ import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.*;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DmpDescriptionTemplateEntity;
import eu.eudat.data.DmpEntity;
import eu.eudat.data.DmpReferenceEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.data.*;
import eu.eudat.model.Dmp;
import eu.eudat.model.PublicDmp;
import eu.eudat.query.utils.QueryUtilsService;
@ -364,6 +361,11 @@ public class DmpQuery extends QueryBase<DmpEntity> {
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
}
if (this.entityDoiQuery != null) {
QueryContext<EntityDoiEntity, UUID> subQuery = this.applySubQuery(this.entityDoiQuery, queryContext, UUID.class, entityDoiEntityRoot -> entityDoiEntityRoot.get(EntityDoiEntity._entityId));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);

View File

@ -27,6 +27,7 @@ public class TenantQuery extends QueryBase<TenantEntity> {
private String like;
private Collection<UUID> ids;
private Collection<String> codes;
private Collection<IsActive> isActives;
private Collection<UUID> excludedIds;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -58,6 +59,21 @@ public class TenantQuery extends QueryBase<TenantEntity> {
return this;
}
public TenantQuery codes(String value) {
this.codes = List.of(value);
return this;
}
public TenantQuery codes(String... value) {
this.codes = Arrays.asList(value);
return this;
}
public TenantQuery codes(Collection<String> values) {
this.codes = values;
return this;
}
public TenantQuery isActive(IsActive value) {
this.isActives = List.of(value);
return this;
@ -95,7 +111,7 @@ public class TenantQuery extends QueryBase<TenantEntity> {
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.isActives);
return this.isEmpty(this.ids) || this.isEmpty(this.codes) ||this.isEmpty(this.isActives);
}
@Override
@ -112,6 +128,12 @@ public class TenantQuery extends QueryBase<TenantEntity> {
predicates.add(inClause);
}
if (this.codes != null) {
CriteriaBuilder.In<String> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantEntity._code));
for (String item : this.codes) inClause.value(item);
predicates.add(inClause);
}
if (this.like != null && !this.like.isEmpty()) {
predicates.add(queryContext.CriteriaBuilder.or(queryUtilsService.ilike(queryContext.CriteriaBuilder, queryContext.Root.get(TenantEntity._code), this.like),
queryUtilsService.ilike(queryContext.CriteriaBuilder, queryContext.Root.get(TenantEntity._name), this.like)

View File

@ -26,7 +26,9 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
private Collection<UUID> excludedIds;
private Collection<UUID> userIds;
private Collection<String> roles;
private Collection<UUID> tenantIds;
private Boolean tenantIsSet;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final UserScope userScope;
@ -81,6 +83,26 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
return this;
}
public UserRoleQuery tenantIds(UUID value) {
this.tenantIds = List.of(value);
return this;
}
public UserRoleQuery tenantIds(UUID... value) {
this.tenantIds = Arrays.asList(value);
return this;
}
public UserRoleQuery tenantIds(Collection<UUID> values) {
this.tenantIds = values;
return this;
}
public UserRoleQuery tenantIsSet(Boolean value) {
this.tenantIsSet = value;
return this;
}
public UserRoleQuery roles(String value) {
this.roles = List.of(value);
return this;
@ -107,6 +129,7 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
this.isEmpty(this.ids) ||
this.isEmpty(this.userIds) ||
this.isEmpty(this.roles) ||
this.isEmpty(this.tenantIds) ||
this.isEmpty(this.excludedIds);
}
@ -144,6 +167,16 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
inClause.value(item);
predicates.add(inClause);
}
if (this.tenantIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserRoleEntity._tenantId));
for (UUID item : this.tenantIds)
inClause.value(item);
predicates.add(inClause);
}
if (this.tenantIsSet != null) {
if (this.tenantIsSet) predicates.add(queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(UserRoleEntity._tenantId)));
else predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(UserRoleEntity._tenantId)));
}
if (this.userIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserRoleEntity._userId));
for (UUID item : this.userIds)
@ -177,6 +210,7 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
else if (item.prefix(UserRole._user)) return UserRoleEntity._userId;
else if (item.match(UserRole._user)) return UserRoleEntity._userId;
else if (item.match(UserRole._createdAt) ) return UserRoleEntity._createdAt;
else if (item.match(UserRoleEntity._tenantId) ) return UserRoleEntity._tenantId;
else if (item.match(UserRole._belongsToCurrentTenant) ) return UserRoleEntity._tenantId;
else return null;
}

View File

@ -2,6 +2,6 @@ package eu.eudat.service.keycloak;
public enum KeycloakRole {
Admin, DescriptionTemplateEditor, Manager, User
Admin, User
}

View File

@ -181,6 +181,7 @@ public class MetricsServiceImpl implements MetricsService {
dmpQuery.after(_config.getNexusDate());
EntityDoiQuery entityDoiQuery = this.queryFactory.query(EntityDoiQuery.class).types(EntityType.DMP).isActive(IsActive.Active);
dmpQuery.entityDoiSubQuery(entityDoiQuery);
dmpQuery.setDistinct(true);
return dmpQuery.count();
}

View File

@ -2,6 +2,7 @@ package eu.eudat.service.user;
import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.AuthorizationProperties;
import eu.eudat.authorization.OwnedResource;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.JsonHandlingService;
@ -11,6 +12,7 @@ import eu.eudat.commons.enums.ActionConfirmationType;
import eu.eudat.commons.enums.ContactInfoType;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.notification.NotificationContactType;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.commons.types.actionconfirmation.MergeAccountConfirmationEntity;
import eu.eudat.commons.types.actionconfirmation.RemoveCredentialRequestEntity;
@ -27,6 +29,7 @@ import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.userremoval.UserRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler;
import eu.eudat.model.Tenant;
import eu.eudat.model.User;
import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential;
@ -107,10 +110,12 @@ public class UserServiceImpl implements UserService {
private final ElasticService elasticService;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler;
private final AuthorizationProperties authorizationProperties;
private final TenantScope tenantScope;
@Autowired
public UserServiceImpl(
TenantEntityManager entityManager,
TenantEntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
@ -120,7 +125,7 @@ public class UserServiceImpl implements UserService {
EventBroker eventBroker,
JsonHandlingService jsonHandlingService,
XmlHandlingService xmlHandlingService, QueryFactory queryFactory,
UserScope userScope, KeycloakService keycloakService, ActionConfirmationService actionConfirmationService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler, ValidatorFactory validatorFactory, ElasticService elasticService, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler) {
UserScope userScope, KeycloakService keycloakService, ActionConfirmationService actionConfirmationService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler, ValidatorFactory validatorFactory, ElasticService elasticService, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler, AuthorizationProperties authorizationProperties, TenantScope tenantScope) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -141,6 +146,8 @@ public class UserServiceImpl implements UserService {
this.elasticService = elasticService;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.userRemovalIntegrationEventHandler = userRemovalIntegrationEventHandler;
this.authorizationProperties = authorizationProperties;
this.tenantScope = tenantScope;
}
//region persist
@ -247,15 +254,27 @@ public class UserServiceImpl implements UserService {
throw new MyApplicationException("Currently cannot update roles for this user");
UUID subjectId = UUID.fromString(userCredentials.getFirst().getExternalId());
this.applyGlobalRoles(data.getId(), subjectId, model);
this.applyTenantRoles(data.getId(), subjectId, model);
List<UserRoleEntity> existingItems = this.queryFactory.query(UserRoleQuery.class).userIds(data.getId()).collect();
this.entityManager.flush();
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
this.userTouchedIntegrationEventHandler.handle(data.getId());
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, User._id), data);
}
private void applyGlobalRoles(UUID userId, UUID subjectId, UserRolePatchPersist model) throws InvalidApplicationException {
List<UserRoleEntity> existingItems = this.queryFactory.query(UserRoleQuery.class).userIds(userId).tenantIsSet(false).roles(this.authorizationProperties.getAllowedGlobalRoles()).collect();
List<UUID> foundIds = new ArrayList<>();
for (String roleName : model.getRoles().stream().filter(x-> x != null && !x.isBlank()).distinct().toList()) {
for (String roleName : model.getRoles().stream().filter(x-> x != null && !x.isBlank() && this.authorizationProperties.getAllowedGlobalRoles().contains(x)).distinct().toList()) {
UserRoleEntity item = existingItems.stream().filter(x-> x.getRole().equals(roleName)).findFirst().orElse(null);
if (item == null) {
item = new UserRoleEntity();
item.setId(UUID.randomUUID());
item.setUserId(data.getId());
item.setUserId(userId);
item.setRole(roleName);
item.setCreatedAt(Instant.now());
this.entityManager.persist(item);
@ -265,17 +284,46 @@ public class UserServiceImpl implements UserService {
}
this.entityManager.flush();
List<UserRoleEntity> toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
toDelete.forEach(x -> this.keycloakService.removeUserFromGroup(subjectId, KeycloakRole.valueOf(x.getRole())));
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
this.entityManager.flush();
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
}
this.userTouchedIntegrationEventHandler.handle(data.getId());
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, User._id), data);
private void applyTenantRoles(UUID userId, UUID subjectId, UserRolePatchPersist model) throws InvalidApplicationException {
if (!tenantScope.isSet()) throw new MyForbiddenException("tenant scope required");
UserRoleQuery userRoleQuery = this.queryFactory.query(UserRoleQuery.class).userIds(userId).roles(this.authorizationProperties.getAllowedTenantRoles());
if (tenantScope.isDefaultTenant()) userRoleQuery.tenantIsSet(false);
else userRoleQuery.tenantIsSet(true).tenantIds(this.tenantScope.getTenant());
List<UserRoleEntity> existingItems = userRoleQuery.collect();
List<UUID> foundIds = new ArrayList<>();
for (String roleName : model.getRoles().stream().filter(x-> x != null && !x.isBlank() && this.authorizationProperties.getAllowedTenantRoles().contains(x)).distinct().toList()) {
UserRoleEntity item = existingItems.stream().filter(x-> x.getRole().equals(roleName)).findFirst().orElse(null);
if (item == null) {
item = new UserRoleEntity();
item.setId(UUID.randomUUID());
item.setUserId(userId);
item.setRole(roleName);
item.setCreatedAt(Instant.now());
item.setTenantId(this.tenantScope.getTenant());
this.entityManager.persist(item);
this.keycloakService.addUserToGroup(subjectId, KeycloakRole.valueOf(roleName));
}
foundIds.add(item.getId());
}
this.entityManager.flush();
List<UserRoleEntity> toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
toDelete.forEach(x -> this.keycloakService.removeUserFromGroup(subjectId, KeycloakRole.valueOf(x.getRole())));
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
this.entityManager.flush();
}
//region mine

View File

@ -45,8 +45,6 @@ public class EntityDoiController {
private final EntityDoiService entityDoiService;
private final DepositService repositoryDepositService;
private final CensorFactory censorFactory;
private final QueryFactory queryFactory;
@ -56,14 +54,12 @@ public class EntityDoiController {
public EntityDoiController(
BuilderFactory builderFactory,
AuditService auditService,
EntityDoiService entityDoiService,
DepositService repositoryDepositService, CensorFactory censorFactory,
EntityDoiService entityDoiService, CensorFactory censorFactory,
QueryFactory queryFactory,
MessageSource messageSource) {
this.builderFactory = builderFactory;
this.auditService = auditService;
this.entityDoiService = entityDoiService;
this.repositoryDepositService = repositoryDepositService;
this.censorFactory = censorFactory;
this.queryFactory = queryFactory;
this.messageSource = messageSource;

View File

@ -25,6 +25,12 @@ permissions:
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
AllowNoTenant:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# public
PublicBrowseDescription:
roles: [ ]
@ -232,26 +238,26 @@ permissions:
# User
BrowseUser:
roles:
- TenantAdmin
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
EditUser:
roles:
- TenantAdmin
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteUser:
roles:
- TenantAdmin
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
ExportUsers:
roles:
- TenantAdmin
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
@ -916,7 +922,7 @@ permissions:
allowAuthenticated: false
ViewUserPage:
roles:
- TenantAdmin
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false

View File

@ -1,7 +1,8 @@
export enum AppRole {
Admin = "Admin",
Manager = "Manager",
User = "User",
DescriptionTemplateEditor = "DescriptionTemplateEditor",
TenantAdmin = "TenantAdmin"
TenantAdmin = "TenantAdmin",
TenantUser = "TenantUser",
TenantManager = "TenantManager",
TenantDescriptionTemplateEditor = "TenantDescriptionTemplateEditor"
}

View File

@ -1,9 +1,9 @@
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
export interface Tenant extends BaseEntity{
name: string;
code: string;
description: string;
name?: string;
code?: string;
description?: string;
config?: TenantConfig;
}
@ -66,4 +66,4 @@ export interface TenantSourcePersist{
export interface SourceCodePersist{
code: string;
}
}

View File

@ -8,7 +8,8 @@ export interface User extends BaseEntity {
name: string;
additionalInfo: UserAdditionalInfo;
contacts: UserContactInfo[];
roles: UserRole[];
globalRoles: UserRole[];
tenantRoles: UserRole[];
credentials: UserCredential[];
}

View File

@ -60,9 +60,10 @@ export class EnumUtils {
switch (status) {
case AppRole.Admin: return this.language.instant('TYPES.APP-ROLE.ADMIN');
case AppRole.User: return this.language.instant('TYPES.APP-ROLE.USER');
case AppRole.Manager: return this.language.instant('TYPES.APP-ROLE.MANAGER');
case AppRole.DescriptionTemplateEditor: return this.language.instant('TYPES.APP-ROLE.DESCRIPTION-TEMPLATE-EDITOR');
case AppRole.TenantAdmin: return this.language.instant('TYPES.APP-ROLE.TENANT-ADMIN');
case AppRole.TenantUser: return this.language.instant('TYPES.APP-ROLE.TENANT-USER');
case AppRole.TenantManager: return this.language.instant('TYPES.APP-ROLE.TENANT-MANAGER');
case AppRole.TenantDescriptionTemplateEditor: return this.language.instant('TYPES.APP-ROLE.TENANT-DESCRIPTION-TEMPLATE-EDITOR');
}
}
@ -311,5 +312,5 @@ export class EnumUtils {
case LockTargetType.DescriptionTemplate: return this.language.instant('TYPES.LOCK-TARGET-TYPE.DESCRIPTION-TEMPLATE');
}
}
}

View File

@ -4,7 +4,7 @@
<div *ngIf="!this.nowEditing"class="roles col-8">
<div *ngFor="let role of this.formGroup.get('roles').value" class="row">
<div class="col-auto p-0">
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'manager': role == appRole.Manager, 'admin': role == appRole.Admin, 'description-template-editor': role == appRole.DescriptionTemplateEditor, 'tenant-admin': role == appRole.TenantAdmin}">
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'tenant-manager': role == appRole.TenantManager, 'admin': role == appRole.Admin, 'tenant-description-template-editor': role == appRole.TenantDescriptionTemplateEditor, 'tenant-admin': role == appRole.TenantAdmin, 'tenant-user': role == appRole.TenantUser}">
{{enumUtils.toAppRoleString(role)}}
</span>
</div>

View File

@ -49,7 +49,7 @@
padding-right: 10px;
}
.manager {
.tenant-manager {
// display: flex;
// justify-content: center;
// align-items: center;
@ -103,7 +103,7 @@
padding-right: 10px;
}
.description-template-editor {
.tenant-description-template-editor {
// display: flex;
// justify-content: center;
// align-items: center;
@ -121,6 +121,43 @@
padding-right: 10px;
}
.tenant-user {
// display: flex;
// justify-content: center;
// align-items: center;
min-width: 67px;
min-height: 28px;
color: #00c4ff;
background: #d3f5ff3a 0% 0% no-repeat padding-box;
border-radius: 44px;
letter-spacing: 0.11px;
font-weight: 400;
opacity: 1;
margin-top: 0.5em;
margin-bottom: 0.5em;
padding-left: 10px;
padding-right: 10px;
}
.tenant-admin {
// display: flex;
// justify-content: center;
// align-items: center;
min-width: 77px;
min-height: 28px;
color: #ff3d33;
background: #ffd5d32f 0% 0% no-repeat padding-box;
border-radius: 44px;
letter-spacing: 0.11px;
font-weight: 400;
opacity: 1;
margin-top: 0.5em;
margin-bottom: 0.5em;
padding-left: 10px;
padding-right: 10px;
}
.save-button {
margin-bottom: 1em;
}

View File

@ -19,6 +19,7 @@ import { FormService } from '@common/forms/form-service';
import { HttpErrorResponse } from '@angular/common/http';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { AppRole } from '@app/core/common/enum/app-role';
import { Guid } from '@common/types/guid';
@Component({
selector: 'app-user-role-editor-component',

View File

@ -20,7 +20,8 @@ export class UserRolePatchEditorModel implements UserRolePatchPersist {
public fromModel(item: User): UserRolePatchEditorModel {
if (item) {
this.id = item.id;
if (item.roles != null)this.roles = item.roles.map(x => x.role);
if (item.globalRoles != null)this.roles = item.globalRoles.map(x => x.role);
if (item.tenantRoles != null) item.tenantRoles.map(x => this.roles.push(x.role));
this.hash = item.hash;
}
return this;
@ -46,4 +47,4 @@ export class UserRolePatchEditorModel implements UserRolePatchPersist {
baseContext.validation = baseValidationArray;
return baseContext;
}
}
}

View File

@ -74,4 +74,4 @@
<div class="col">{{row.name}}</div>
</div>
</ng-template>
</ng-template>

View File

@ -2,10 +2,13 @@ import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { Tenant } from '@app/core/model/tenant/tenant';
import { User, UserAdditionalInfo, UserContactInfo, UserRole } from '@app/core/model/user/user';
import { TenantLookup } from '@app/core/query/tenant.lookup';
import { UserLookup } from '@app/core/query/user.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { TenantService } from '@app/core/services/tenant/tenant.service';
import { UserService } from '@app/core/services/user/user.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
@ -44,8 +47,10 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.id)].join('.'),
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.type)].join('.'),
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.value)].join('.'),
[nameof<User>(x => x.roles), nameof<UserRole>(x => x.id)].join('.'),
[nameof<User>(x => x.roles), nameof<UserRole>(x => x.role)].join('.'),
[nameof<User>(x => x.globalRoles), nameof<UserRole>(x => x.id)].join('.'),
[nameof<User>(x => x.globalRoles), nameof<UserRole>(x => x.role)].join('.'),
[nameof<User>(x => x.tenantRoles), nameof<UserRole>(x => x.id)].join('.'),
[nameof<User>(x => x.tenantRoles), nameof<UserRole>(x => x.role)].join('.'),
[nameof<User>(x => x.additionalInfo), nameof<UserAdditionalInfo>(x => x.avatarUrl)].join('.'),
nameof<User>(x => x.updatedAt),
nameof<User>(x => x.createdAt),
@ -55,6 +60,7 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
rowIdentity = x => x.id;
constructor(
protected router: Router,
protected route: ActivatedRoute,
@ -120,7 +126,7 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
prop: nameof<User>(x => x.roles),
prop: nameof<User>(x => x.globalRoles),
languageName: 'USER-LISTING.FIELDS.ROLES',
alwaysShown: true,
maxWidth: 300,

View File

@ -10,6 +10,7 @@ import { UserListingFiltersComponent } from './listing/filters/user-listing-filt
import { UserListingComponent } from './listing/user-listing.component';
import { UsersRoutingModule } from './user.routing';
import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.component';
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
@NgModule({
declarations: [
@ -25,6 +26,7 @@ import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.
CommonFormattingModule,
UsersRoutingModule,
HybridListingModule,
AutoCompleteModule,
TextFilterModule,
UserSettingsModule
]

View File

@ -196,8 +196,8 @@
</div>
</ng-container>
<ng-container *ngIf="canCreateNewVersion()">
<div class="col-12 d-flex align-items-center">
<button mat-mini-fab class="frame-btn" (click)="newVersionClicked()">
<div class="col-12 d-flex align-items-center" (click)="newVersionClicked()">
<button mat-mini-fab class="frame-btn">
<mat-icon class="mat-mini-fab-icon">add_to_photos</mat-icon>
</button>
<p class="mb-0 pl-2 frame-txt">{{ 'DMP-OVERVIEW.ACTIONS.NEW-VERSION' | translate }}</p>

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Administrator",
"USER": "Benutzer",
"MANAGER": "Manager",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Administrator",
"TENANT-USER": "Tenant Benutzer",
"TENANT-MANAGER": "Manager",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1577,9 +1577,10 @@
"APP-ROLE": {
"ADMIN": "Admin",
"USER": "User",
"MANAGER": "Manager",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor",
"TENANT-ADMIN": "Tenant Admin"
"TENANT-ADMIN": "Tenant Admin",
"TENANT-USER": "Tenant User",
"TENANT-MANAGER": "Manager",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"IS-ACTIVE": {
"ACTIVE": "Active",
@ -1713,7 +1714,7 @@
"INTERNAL": "Internal",
"EXTERNAL": "External"
},
"NOTIFICATION-TEMPLATE-KIND": {
"NOTIFICATION-TEMPLATE-KIND": {
"DRAFT": "Draft",
"SECONDARY": "Secondary",
"PRIMARY": "Primary",
@ -2010,4 +2011,4 @@
"CANCEL": "Cancel"
}
}
}
}

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Administrador",
"USER": "Usuario",
"MANAGER": "Director",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Administrador",
"TENANT-USER": "Tenant Usuario",
"TENANT-MANAGER": "Director",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Διαχειριστής",
"USER": "Χρήστης",
"MANAGER": "Υπεύθυνος",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Διαχειριστής Teannt",
"TENANT-USER": "Χρήστης Teannt",
"TENANT-MANAGER": "Υπεύθυνος",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Administrator",
"USER": "Korisnik",
"MANAGER": "Upravitelj",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Administrator",
"TENANT-USER": "Tenant Korisnik",
"TENANT-MANAGER": "Upravitelj",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Administrator",
"USER": "Użytkownik",
"MANAGER": "Menedżer",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Administrator",
"TENANT-USER": "Tenant Użytkownik",
"TENANT-MANAGER": "Menedżer",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Administrador",
"USER": "Utilizador",
"MANAGER": "Gestor",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Administrador",
"TENANT-USER": "Tenant Utilizador",
"TENANT-MANAGER": "Gestor",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Admin",
"USER": "Používateľ",
"MANAGER": "Manažér",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Admin",
"TENANT-USER": "Tenant Používateľ",
"TENANT-MANAGER": "Manažér",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Administrator",
"USER": "Korisnik",
"MANAGER": "Menadžer",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Admin",
"TENANT-USER": "Tenant Korisnik",
"TENANT-MANAGER": "Menadžer",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {

View File

@ -1429,8 +1429,10 @@
"APP-ROLE": {
"ADMIN": "Admin",
"USER": "Kullanıcı",
"MANAGER": "Yönetici",
"DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
"TENANT-ADMIN": "Tenant Admin",
"TENANT-USER": "Tenant Kullanıcı",
"TENANT-MANAGER": "Yönetici",
"TENANT-DESCRIPTION-TEMPLATE-EDITOR": "Description Template Editor"
},
"DMP-PROFILE-FIELD": {
"DATA-TYPE": {