user role page changes

This commit is contained in:
Efstratios Giannopoulos 2024-04-16 11:02:17 +03:00
parent 0cf61944dc
commit f3282c7fc6
31 changed files with 330 additions and 88 deletions

View File

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

View File

@ -39,9 +39,13 @@ public class User {
public static final String _contacts = "contacts"; 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; private List<UserCredential> credentials;
@ -114,14 +118,6 @@ public class User {
this.contacts = contacts; this.contacts = contacts;
} }
public List<UserRole> getRoles() {
return roles;
}
public void setRoles(List<UserRole> roles) {
this.roles = roles;
}
public List<UserCredential> getCredentials() { public List<UserCredential> getCredentials() {
return credentials; return credentials;
} }
@ -137,4 +133,20 @@ public class User {
public void setTenantUsers(List<TenantUser> tenantUsers) { public void setTenantUsers(List<TenantUser> tenantUsers) {
this.tenantUsers = 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; package eu.eudat.model.builder;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.AuthorizationProperties;
import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.types.user.AdditionalInfoEntity; import eu.eudat.commons.types.user.AdditionalInfoEntity;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.data.UserEntity; import eu.eudat.data.UserEntity;
import eu.eudat.model.*; import eu.eudat.model.*;
import eu.eudat.query.TenantUserQuery; import eu.eudat.query.*;
import eu.eudat.query.UserContactInfoQuery;
import eu.eudat.query.UserCredentialQuery;
import eu.eudat.query.UserRoleQuery;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException; 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.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry; import gr.cite.tools.logging.DataLogEntry;
@ -20,9 +23,12 @@ import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -34,17 +40,26 @@ public class UserBuilder extends BaseBuilder<User, UserEntity> {
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final JsonHandlingService jsonHandlingService; 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); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired @Autowired
public UserBuilder(ConventionService conventionService, public UserBuilder(ConventionService conventionService,
QueryFactory queryFactory, 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))); super(conventionService, new LoggerService(LoggerFactory.getLogger(UserBuilder.class)));
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.authorizationProperties = authorizationProperties;
this.tenantEntityManager = tenantEntityManager;
this.tenantScope = tenantScope;
this.messageSource = messageSource;
} }
public UserBuilder authorize(EnumSet<AuthorizationFlags> values) { 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)); FieldSet contactsFields = fields.extractPrefixed(this.asPrefix(User._contacts));
Map<UUID, List<UserContactInfo>> contactsMap = this.collectUserContactInfos(contactsFields, data); Map<UUID, List<UserContactInfo>> contactsMap = this.collectUserContactInfos(contactsFields, data);
FieldSet rolesFields = fields.extractPrefixed(this.asPrefix(User._roles)); FieldSet globalRolesFields = fields.extractPrefixed(this.asPrefix(User._globalRoles));
Map<UUID, List<UserRole>> rolesMap = this.collectUserRoles(rolesFields, data); 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)); FieldSet credentialsFields = fields.extractPrefixed(this.asPrefix(User._credentials));
Map<UUID, List<UserCredential>> credentialsMap = this.collectUserCredentials(credentialsFields, data); 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._isActive))) m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(User._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); 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 (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 (credentialsMap != null && !credentialsFields.isEmpty() && credentialsMap.containsKey(d.getId())) m.setCredentials(credentialsMap.get(d.getId()));
if (!additionalInfoFields.isEmpty() && d.getAdditionalInfo() != null){ if (!additionalInfoFields.isEmpty() && d.getAdditionalInfo() != null){
AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, d.getAdditionalInfo()); AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, d.getAdditionalInfo());
@ -115,13 +134,42 @@ public class UserBuilder extends BaseBuilder<User, UserEntity> {
return itemMap; 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; if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", UserRole.class.getSimpleName()); this.logger.debug("checking related - {}", UserRole.class.getSimpleName());
Map<UUID, List<UserRole>> itemMap; Map<UUID, List<UserRole>> itemMap;
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(UserRole._user, User._id)); 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()); 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))) { if (!fields.hasField(this.asIndexer(UserRole._user, User._id))) {

View File

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

View File

@ -27,6 +27,7 @@ public class TenantQuery extends QueryBase<TenantEntity> {
private String like; private String like;
private Collection<UUID> ids; private Collection<UUID> ids;
private Collection<String> codes;
private Collection<IsActive> isActives; private Collection<IsActive> isActives;
private Collection<UUID> excludedIds; private Collection<UUID> excludedIds;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -58,6 +59,21 @@ public class TenantQuery extends QueryBase<TenantEntity> {
return this; 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) { public TenantQuery isActive(IsActive value) {
this.isActives = List.of(value); this.isActives = List.of(value);
return this; return this;
@ -95,7 +111,7 @@ public class TenantQuery extends QueryBase<TenantEntity> {
@Override @Override
protected Boolean isFalseQuery() { 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 @Override
@ -112,6 +128,12 @@ public class TenantQuery extends QueryBase<TenantEntity> {
predicates.add(inClause); 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()) { if (this.like != null && !this.like.isEmpty()) {
predicates.add(queryContext.CriteriaBuilder.or(queryUtilsService.ilike(queryContext.CriteriaBuilder, queryContext.Root.get(TenantEntity._code), this.like), 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) 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> excludedIds;
private Collection<UUID> userIds; private Collection<UUID> userIds;
private Collection<String> roles; private Collection<String> roles;
private Collection<UUID> tenantIds;
private Boolean tenantIsSet;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final UserScope userScope; private final UserScope userScope;
@ -81,6 +83,26 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
return this; 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) { public UserRoleQuery roles(String value) {
this.roles = List.of(value); this.roles = List.of(value);
return this; return this;
@ -107,6 +129,7 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
this.isEmpty(this.ids) || this.isEmpty(this.ids) ||
this.isEmpty(this.userIds) || this.isEmpty(this.userIds) ||
this.isEmpty(this.roles) || this.isEmpty(this.roles) ||
this.isEmpty(this.tenantIds) ||
this.isEmpty(this.excludedIds); this.isEmpty(this.excludedIds);
} }
@ -144,6 +167,16 @@ public class UserRoleQuery extends QueryBase<UserRoleEntity> {
inClause.value(item); inClause.value(item);
predicates.add(inClause); 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) { if (this.userIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserRoleEntity._userId)); CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserRoleEntity._userId));
for (UUID item : this.userIds) 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.prefix(UserRole._user)) return UserRoleEntity._userId;
else if (item.match(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(UserRole._createdAt) ) return UserRoleEntity._createdAt;
else if (item.match(UserRoleEntity._tenantId) ) return UserRoleEntity._tenantId;
else if (item.match(UserRole._belongsToCurrentTenant) ) return UserRoleEntity._tenantId; else if (item.match(UserRole._belongsToCurrentTenant) ) return UserRoleEntity._tenantId;
else return null; else return null;
} }

View File

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

View File

@ -2,6 +2,7 @@ package eu.eudat.service.user;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.AuthorizationProperties;
import eu.eudat.authorization.OwnedResource; import eu.eudat.authorization.OwnedResource;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.commons.JsonHandlingService; 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.ContactInfoType;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.notification.NotificationContactType; 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.scope.user.UserScope;
import eu.eudat.commons.types.actionconfirmation.MergeAccountConfirmationEntity; import eu.eudat.commons.types.actionconfirmation.MergeAccountConfirmationEntity;
import eu.eudat.commons.types.actionconfirmation.RemoveCredentialRequestEntity; 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.notification.NotifyIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.userremoval.UserRemovalIntegrationEventHandler; import eu.eudat.integrationevent.outbox.userremoval.UserRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler; import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler;
import eu.eudat.model.Tenant;
import eu.eudat.model.User; import eu.eudat.model.User;
import eu.eudat.model.UserContactInfo; import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential; import eu.eudat.model.UserCredential;
@ -107,10 +110,12 @@ public class UserServiceImpl implements UserService {
private final ElasticService elasticService; private final ElasticService elasticService;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler; private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler; private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler;
private final AuthorizationProperties authorizationProperties;
private final TenantScope tenantScope;
@Autowired @Autowired
public UserServiceImpl( public UserServiceImpl(
TenantEntityManager entityManager, TenantEntityManager entityManager,
AuthorizationService authorizationService, AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
BuilderFactory builderFactory, BuilderFactory builderFactory,
@ -120,7 +125,7 @@ public class UserServiceImpl implements UserService {
EventBroker eventBroker, EventBroker eventBroker,
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
XmlHandlingService xmlHandlingService, QueryFactory queryFactory, 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.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -141,6 +146,8 @@ public class UserServiceImpl implements UserService {
this.elasticService = elasticService; this.elasticService = elasticService;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler; this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.userRemovalIntegrationEventHandler = userRemovalIntegrationEventHandler; this.userRemovalIntegrationEventHandler = userRemovalIntegrationEventHandler;
this.authorizationProperties = authorizationProperties;
this.tenantScope = tenantScope;
} }
//region persist //region persist
@ -247,15 +254,27 @@ public class UserServiceImpl implements UserService {
throw new MyApplicationException("Currently cannot update roles for this user"); throw new MyApplicationException("Currently cannot update roles for this user");
UUID subjectId = UUID.fromString(userCredentials.getFirst().getExternalId()); 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<>(); 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); UserRoleEntity item = existingItems.stream().filter(x-> x.getRole().equals(roleName)).findFirst().orElse(null);
if (item == null) { if (item == null) {
item = new UserRoleEntity(); item = new UserRoleEntity();
item.setId(UUID.randomUUID()); item.setId(UUID.randomUUID());
item.setUserId(data.getId()); item.setUserId(userId);
item.setRole(roleName); item.setRole(roleName);
item.setCreatedAt(Instant.now()); item.setCreatedAt(Instant.now());
this.entityManager.persist(item); this.entityManager.persist(item);
@ -265,17 +284,46 @@ public class UserServiceImpl implements UserService {
} }
this.entityManager.flush(); this.entityManager.flush();
List<UserRoleEntity> toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList()); 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()))); toDelete.forEach(x -> this.keycloakService.removeUserFromGroup(subjectId, KeycloakRole.valueOf(x.getRole())));
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete); this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
this.entityManager.flush(); this.entityManager.flush();
}
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
this.userTouchedIntegrationEventHandler.handle(data.getId()); private void applyTenantRoles(UUID userId, UUID subjectId, UserRolePatchPersist model) throws InvalidApplicationException {
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, User._id), data); 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 //region mine

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,8 +60,10 @@ export class EnumUtils {
switch (status) { switch (status) {
case AppRole.Admin: return this.language.instant('TYPES.APP-ROLE.ADMIN'); case AppRole.Admin: return this.language.instant('TYPES.APP-ROLE.ADMIN');
case AppRole.User: return this.language.instant('TYPES.APP-ROLE.USER'); case AppRole.User: return this.language.instant('TYPES.APP-ROLE.USER');
case AppRole.Manager: return this.language.instant('TYPES.APP-ROLE.MANAGER'); case AppRole.TenantAdmin: return this.language.instant('TYPES.APP-ROLE.TENANT-ADMIN');
case AppRole.DescriptionTemplateEditor: return this.language.instant('TYPES.APP-ROLE.DESCRIPTION-TEMPLATE-EDITOR'); 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');
} }
} }
@ -310,5 +312,5 @@ export class EnumUtils {
case LockTargetType.DescriptionTemplate: return this.language.instant('TYPES.LOCK-TARGET-TYPE.DESCRIPTION-TEMPLATE'); case LockTargetType.DescriptionTemplate: return this.language.instant('TYPES.LOCK-TARGET-TYPE.DESCRIPTION-TEMPLATE');
} }
} }
} }

View File

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

View File

@ -49,7 +49,7 @@
padding-right: 10px; padding-right: 10px;
} }
.manager { .tenant-manager {
// display: flex; // display: flex;
// justify-content: center; // justify-content: center;
// align-items: center; // align-items: center;
@ -85,7 +85,7 @@
padding-right: 10px; padding-right: 10px;
} }
.description-template-editor { .tenant-description-template-editor {
// display: flex; // display: flex;
// justify-content: center; // justify-content: center;
// align-items: center; // align-items: center;
@ -103,6 +103,43 @@
padding-right: 10px; 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 { .save-button {
margin-bottom: 1em; margin-bottom: 1em;
} }

View File

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

View File

@ -20,7 +20,8 @@ export class UserRolePatchEditorModel implements UserRolePatchPersist {
public fromModel(item: User): UserRolePatchEditorModel { public fromModel(item: User): UserRolePatchEditorModel {
if (item) { if (item) {
this.id = item.id; 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; this.hash = item.hash;
} }
return this; return this;
@ -46,4 +47,4 @@ export class UserRolePatchEditorModel implements UserRolePatchPersist {
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
} }

View File

@ -74,4 +74,4 @@
<div class="col">{{row.name}}</div> <div class="col">{{row.name}}</div>
</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 { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { IsActive } from '@app/core/common/enum/is-active.enum'; 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 { 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 { UserLookup } from '@app/core/query/user.lookup';
import { AuthService } from '@app/core/services/auth/auth.service'; import { AuthService } from '@app/core/services/auth/auth.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-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 { UserService } from '@app/core/services/user/user.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-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.id)].join('.'),
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.type)].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.contacts), nameof<UserContactInfo>(x => x.value)].join('.'),
[nameof<User>(x => x.roles), nameof<UserRole>(x => x.id)].join('.'), [nameof<User>(x => x.globalRoles), 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.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.additionalInfo), nameof<UserAdditionalInfo>(x => x.avatarUrl)].join('.'),
nameof<User>(x => x.updatedAt), nameof<User>(x => x.updatedAt),
nameof<User>(x => x.createdAt), nameof<User>(x => x.createdAt),
@ -55,6 +60,7 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
rowIdentity = x => x.id; rowIdentity = x => x.id;
constructor( constructor(
protected router: Router, protected router: Router,
protected route: ActivatedRoute, protected route: ActivatedRoute,
@ -120,7 +126,7 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short') 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', languageName: 'USER-LISTING.FIELDS.ROLES',
alwaysShown: true, alwaysShown: true,
maxWidth: 300, maxWidth: 300,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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