diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/scope/tenant/TenantScope.java b/dmp-backend/core/src/main/java/eu/eudat/commons/scope/tenant/TenantScope.java index 52a884d0d..1a6b104c6 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/commons/scope/tenant/TenantScope.java +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/scope/tenant/TenantScope.java @@ -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) diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/User.java b/dmp-backend/core/src/main/java/eu/eudat/model/User.java index d9e775a60..9105be8c3 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/User.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/User.java @@ -39,9 +39,13 @@ public class User { public static final String _contacts = "contacts"; - private List roles; + private List globalRoles; - public static final String _roles = "roles"; + public static final String _globalRoles = "globalRoles"; + + private List tenantRoles; + + public static final String _tenantRoles = "tenantRoles"; private List credentials; @@ -114,14 +118,6 @@ public class User { this.contacts = contacts; } - public List getRoles() { - return roles; - } - - public void setRoles(List roles) { - this.roles = roles; - } - public List getCredentials() { return credentials; } @@ -137,4 +133,20 @@ public class User { public void setTenantUsers(List tenantUsers) { this.tenantUsers = tenantUsers; } + + public List getTenantRoles() { + return tenantRoles; + } + + public void setTenantRoles(List tenantRoles) { + this.tenantRoles = tenantRoles; + } + + public List getGlobalRoles() { + return globalRoles; + } + + public void setGlobalRoles(List globalRoles) { + this.globalRoles = globalRoles; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java index 46d613232..59780b7e9 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java @@ -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 { 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 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 values) { @@ -64,8 +79,11 @@ public class UserBuilder extends BaseBuilder { FieldSet contactsFields = fields.extractPrefixed(this.asPrefix(User._contacts)); Map> contactsMap = this.collectUserContactInfos(contactsFields, data); - FieldSet rolesFields = fields.extractPrefixed(this.asPrefix(User._roles)); - Map> rolesMap = this.collectUserRoles(rolesFields, data); + FieldSet globalRolesFields = fields.extractPrefixed(this.asPrefix(User._globalRoles)); + Map> globalRolesMap = this.collectUserGlobalRoles(globalRolesFields, data); + + FieldSet tenantRolesFields = fields.extractPrefixed(this.asPrefix(User._tenantRoles)); + Map> tenantRolesMap = this.collectUserTenantRoles(tenantRolesFields, data); FieldSet credentialsFields = fields.extractPrefixed(this.asPrefix(User._credentials)); Map> credentialsMap = this.collectUserCredentials(credentialsFields, data); @@ -83,7 +101,8 @@ public class UserBuilder extends BaseBuilder { 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 { return itemMap; } - private Map> collectUserRoles(FieldSet fields, List data) throws MyApplicationException { + private Map> collectUserGlobalRoles(FieldSet fields, List data) throws MyApplicationException { if (fields.isEmpty() || data.isEmpty()) return null; this.logger.debug("checking related - {}", UserRole.class.getSimpleName()); Map> 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> collectUserTenantRoles(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) return null; + this.logger.debug("checking related - {}", UserRole.class.getSimpleName()); + Map> 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))) { diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/UserCensor.java b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/UserCensor.java index 8ebd75ecf..e45857a0f 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/UserCensor.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/UserCensor.java @@ -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); diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java index 0b70cd869..5b6f6fe6c 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java @@ -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 { predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query)); } + if (this.entityDoiQuery != null) { + QueryContext 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); diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java index f1acf6167..5e7d0f086 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/TenantQuery.java @@ -27,6 +27,7 @@ public class TenantQuery extends QueryBase { private String like; private Collection ids; + private Collection codes; private Collection isActives; private Collection excludedIds; private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); @@ -58,6 +59,21 @@ public class TenantQuery extends QueryBase { 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 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 { @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 { predicates.add(inClause); } + if (this.codes != null) { + CriteriaBuilder.In 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) diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java index 4b3de7573..d80c704f8 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java @@ -26,7 +26,9 @@ public class UserRoleQuery extends QueryBase { private Collection excludedIds; private Collection userIds; private Collection roles; - + private Collection tenantIds; + private Boolean tenantIsSet; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); private final UserScope userScope; @@ -81,6 +83,26 @@ public class UserRoleQuery extends QueryBase { 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 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 { 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 { inClause.value(item); predicates.add(inClause); } + if (this.tenantIds != null) { + CriteriaBuilder.In 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 inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserRoleEntity._userId)); for (UUID item : this.userIds) @@ -177,6 +210,7 @@ public class UserRoleQuery extends QueryBase { 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; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/keycloak/KeycloakRole.java b/dmp-backend/core/src/main/java/eu/eudat/service/keycloak/KeycloakRole.java index bb509ed57..c79afbc39 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/keycloak/KeycloakRole.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/keycloak/KeycloakRole.java @@ -2,6 +2,6 @@ package eu.eudat.service.keycloak; public enum KeycloakRole { - Admin, DescriptionTemplateEditor, Manager, User + Admin, User } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/metrics/MetricsServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/metrics/MetricsServiceImpl.java index 71983ce21..80ee709d9 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/metrics/MetricsServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/metrics/MetricsServiceImpl.java @@ -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(); } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java index 4706d42c4..4be24d789 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java @@ -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 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 existingItems = this.queryFactory.query(UserRoleQuery.class).userIds(userId).tenantIsSet(false).roles(this.authorizationProperties.getAllowedGlobalRoles()).collect(); List 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 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 existingItems = userRoleQuery.collect(); + List 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 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 diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/EntityDoiController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/EntityDoiController.java index 86e2cfc10..156339675 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/EntityDoiController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/EntityDoiController.java @@ -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; diff --git a/dmp-backend/web/src/main/resources/config/permissions.yml b/dmp-backend/web/src/main/resources/config/permissions.yml index 2142eeca2..306471c24 100644 --- a/dmp-backend/web/src/main/resources/config/permissions.yml +++ b/dmp-backend/web/src/main/resources/config/permissions.yml @@ -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 diff --git a/dmp-frontend/src/app/core/common/enum/app-role.ts b/dmp-frontend/src/app/core/common/enum/app-role.ts index 30579da65..1213fb136 100644 --- a/dmp-frontend/src/app/core/common/enum/app-role.ts +++ b/dmp-frontend/src/app/core/common/enum/app-role.ts @@ -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" } diff --git a/dmp-frontend/src/app/core/model/tenant/tenant.ts b/dmp-frontend/src/app/core/model/tenant/tenant.ts index 114e95fc8..8c522db49 100644 --- a/dmp-frontend/src/app/core/model/tenant/tenant.ts +++ b/dmp-frontend/src/app/core/model/tenant/tenant.ts @@ -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; -} \ No newline at end of file +} diff --git a/dmp-frontend/src/app/core/model/user/user.ts b/dmp-frontend/src/app/core/model/user/user.ts index fdf48441a..fa39e12cf 100644 --- a/dmp-frontend/src/app/core/model/user/user.ts +++ b/dmp-frontend/src/app/core/model/user/user.ts @@ -8,7 +8,8 @@ export interface User extends BaseEntity { name: string; additionalInfo: UserAdditionalInfo; contacts: UserContactInfo[]; - roles: UserRole[]; + globalRoles: UserRole[]; + tenantRoles: UserRole[]; credentials: UserCredential[]; } diff --git a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts index efbb64600..8171f4027 100644 --- a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts +++ b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts @@ -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'); } } - + } diff --git a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html index 98fa906a2..10c4b10b9 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html @@ -4,7 +4,7 @@
- + {{enumUtils.toAppRoleString(role)}}
diff --git a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss index 7aa863d71..5d9c920fa 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss +++ b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss @@ -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; } diff --git a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.ts b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.ts index 214555534..cdc8ebe55 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.ts @@ -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', diff --git a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.model.ts b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.model.ts index ef01c5ebf..3bb486681 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.model.ts @@ -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; } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html index 8f858355f..8c32e22ca 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html @@ -74,4 +74,4 @@
{{row.name}}
- \ No newline at end of file + diff --git a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts index 1e69a7131..7ffe83ca4 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts @@ -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 [nameof(x => x.contacts), nameof(x => x.id)].join('.'), [nameof(x => x.contacts), nameof(x => x.type)].join('.'), [nameof(x => x.contacts), nameof(x => x.value)].join('.'), - [nameof(x => x.roles), nameof(x => x.id)].join('.'), - [nameof(x => x.roles), nameof(x => x.role)].join('.'), + [nameof(x => x.globalRoles), nameof(x => x.id)].join('.'), + [nameof(x => x.globalRoles), nameof(x => x.role)].join('.'), + [nameof(x => x.tenantRoles), nameof(x => x.id)].join('.'), + [nameof(x => x.tenantRoles), nameof(x => x.role)].join('.'), [nameof(x => x.additionalInfo), nameof(x => x.avatarUrl)].join('.'), nameof(x => x.updatedAt), nameof(x => x.createdAt), @@ -55,6 +60,7 @@ export class UserListingComponent extends BaseListingComponent rowIdentity = x => x.id; + constructor( protected router: Router, protected route: ActivatedRoute, @@ -120,7 +126,7 @@ export class UserListingComponent extends BaseListingComponent pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') }, { - prop: nameof(x => x.roles), + prop: nameof(x => x.globalRoles), languageName: 'USER-LISTING.FIELDS.ROLES', alwaysShown: true, maxWidth: 300, diff --git a/dmp-frontend/src/app/ui/admin/user/user.module.ts b/dmp-frontend/src/app/ui/admin/user/user.module.ts index aaa36defe..65b22e9a6 100644 --- a/dmp-frontend/src/app/ui/admin/user/user.module.ts +++ b/dmp-frontend/src/app/ui/admin/user/user.module.ts @@ -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 ] diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html index 21266d8f1..e0779fe4a 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html @@ -196,8 +196,8 @@
-
-

{{ 'DMP-OVERVIEW.ACTIONS.NEW-VERSION' | translate }}

diff --git a/dmp-frontend/src/assets/i18n/de.json b/dmp-frontend/src/assets/i18n/de.json index cf82e452a..3c7abed97 100644 --- a/dmp-frontend/src/assets/i18n/de.json +++ b/dmp-frontend/src/assets/i18n/de.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 711807598..e61837759 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -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" } } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/i18n/es.json b/dmp-frontend/src/assets/i18n/es.json index 6c739315f..26219253a 100644 --- a/dmp-frontend/src/assets/i18n/es.json +++ b/dmp-frontend/src/assets/i18n/es.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/gr.json b/dmp-frontend/src/assets/i18n/gr.json index e6379505f..004286dc7 100644 --- a/dmp-frontend/src/assets/i18n/gr.json +++ b/dmp-frontend/src/assets/i18n/gr.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/hr.json b/dmp-frontend/src/assets/i18n/hr.json index 29eaac943..ee8b99a8d 100644 --- a/dmp-frontend/src/assets/i18n/hr.json +++ b/dmp-frontend/src/assets/i18n/hr.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/pl.json b/dmp-frontend/src/assets/i18n/pl.json index 4e66a6aa2..55f92f947 100644 --- a/dmp-frontend/src/assets/i18n/pl.json +++ b/dmp-frontend/src/assets/i18n/pl.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/pt.json b/dmp-frontend/src/assets/i18n/pt.json index f25461fb2..d530808f1 100644 --- a/dmp-frontend/src/assets/i18n/pt.json +++ b/dmp-frontend/src/assets/i18n/pt.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/sk.json b/dmp-frontend/src/assets/i18n/sk.json index f67c3a2f5..c00ccb39a 100644 --- a/dmp-frontend/src/assets/i18n/sk.json +++ b/dmp-frontend/src/assets/i18n/sk.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/sr.json b/dmp-frontend/src/assets/i18n/sr.json index 2944ce3fd..0421c780f 100644 --- a/dmp-frontend/src/assets/i18n/sr.json +++ b/dmp-frontend/src/assets/i18n/sr.json @@ -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": { diff --git a/dmp-frontend/src/assets/i18n/tr.json b/dmp-frontend/src/assets/i18n/tr.json index 06628424b..9587dfd30 100644 --- a/dmp-frontend/src/assets/i18n/tr.json +++ b/dmp-frontend/src/assets/i18n/tr.json @@ -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": {