unlink merge fixes
This commit is contained in:
parent
62120517b1
commit
9e1cf8f642
|
@ -10,6 +10,11 @@ public class AuthorizationProperties {
|
|||
|
||||
private String globalAdminRole;
|
||||
private String tenantAdminRole;
|
||||
private String globalUserRole;
|
||||
private String tenantUserRole;
|
||||
private Boolean autoAssignGlobalAdminToNewTenants;
|
||||
private List<String> allowedTenantRoles;
|
||||
private List<String> allowedGlobalRoles;
|
||||
|
||||
public String getGlobalAdminRole() {
|
||||
return this.globalAdminRole;
|
||||
|
@ -18,35 +23,6 @@ public class AuthorizationProperties {
|
|||
public void setGlobalAdminRole(String globalAdminRole) {
|
||||
this.globalAdminRole = globalAdminRole;
|
||||
}
|
||||
private Boolean autoAssignGlobalAdminToNewTenants;
|
||||
|
||||
public Boolean getAutoAssignGlobalAdminToNewTenants() {
|
||||
return this.autoAssignGlobalAdminToNewTenants;
|
||||
}
|
||||
|
||||
public void setAutoAssignGlobalAdminToNewTenants(Boolean autoAssignGlobalAdminToNewTenants) {
|
||||
this.autoAssignGlobalAdminToNewTenants = autoAssignGlobalAdminToNewTenants;
|
||||
}
|
||||
|
||||
private List<String> allowedTenantRoles;
|
||||
|
||||
public List<String> getAllowedTenantRoles() {
|
||||
return this.allowedTenantRoles;
|
||||
}
|
||||
|
||||
public void setAllowedTenantRoles(List<String> allowedTenantRoles) {
|
||||
this.allowedTenantRoles = allowedTenantRoles;
|
||||
}
|
||||
|
||||
private List<String> allowedGlobalRoles;
|
||||
|
||||
public List<String> getAllowedGlobalRoles() {
|
||||
return this.allowedGlobalRoles;
|
||||
}
|
||||
|
||||
public void setAllowedGlobalRoles(List<String> allowedGlobalRoles) {
|
||||
this.allowedGlobalRoles = allowedGlobalRoles;
|
||||
}
|
||||
|
||||
public String getTenantAdminRole() {
|
||||
return this.tenantAdminRole;
|
||||
|
@ -55,4 +31,44 @@ public class AuthorizationProperties {
|
|||
public void setTenantAdminRole(String tenantAdminRole) {
|
||||
this.tenantAdminRole = tenantAdminRole;
|
||||
}
|
||||
|
||||
public String getGlobalUserRole() {
|
||||
return this.globalUserRole;
|
||||
}
|
||||
|
||||
public void setGlobalUserRole(String globalUserRole) {
|
||||
this.globalUserRole = globalUserRole;
|
||||
}
|
||||
|
||||
public String getTenantUserRole() {
|
||||
return this.tenantUserRole;
|
||||
}
|
||||
|
||||
public void setTenantUserRole(String tenantUserRole) {
|
||||
this.tenantUserRole = tenantUserRole;
|
||||
}
|
||||
|
||||
public Boolean getAutoAssignGlobalAdminToNewTenants() {
|
||||
return this.autoAssignGlobalAdminToNewTenants;
|
||||
}
|
||||
|
||||
public void setAutoAssignGlobalAdminToNewTenants(Boolean autoAssignGlobalAdminToNewTenants) {
|
||||
this.autoAssignGlobalAdminToNewTenants = autoAssignGlobalAdminToNewTenants;
|
||||
}
|
||||
|
||||
public List<String> getAllowedTenantRoles() {
|
||||
return this.allowedTenantRoles;
|
||||
}
|
||||
|
||||
public void setAllowedTenantRoles(List<String> allowedTenantRoles) {
|
||||
this.allowedTenantRoles = allowedTenantRoles;
|
||||
}
|
||||
|
||||
public List<String> getAllowedGlobalRoles() {
|
||||
return this.allowedGlobalRoles;
|
||||
}
|
||||
|
||||
public void setAllowedGlobalRoles(List<String> allowedGlobalRoles) {
|
||||
this.allowedGlobalRoles = allowedGlobalRoles;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@ public class LocaleConfiguration {
|
|||
}
|
||||
|
||||
public LocaleProperties getProperties() {
|
||||
return properties;
|
||||
return this.properties;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,15 @@ public class MultitenancyProperties {
|
|||
private String defaultTenantCode;
|
||||
|
||||
public boolean isMultitenant() {
|
||||
return isMultitenant;
|
||||
return this.isMultitenant;
|
||||
}
|
||||
|
||||
public void setIsMultitenant(boolean multitenant) {
|
||||
isMultitenant = multitenant;
|
||||
this.isMultitenant = multitenant;
|
||||
}
|
||||
|
||||
public String getDefaultTenantCode() {
|
||||
return defaultTenantCode;
|
||||
return this.defaultTenantCode;
|
||||
}
|
||||
|
||||
public void setDefaultTenantCode(String defaultTenantCode) {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package org.opencdmp.commons.scope.tenant;
|
||||
|
||||
import org.opencdmp.data.tenant.TenantScopedBaseEntity;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.hibernate.Session;
|
||||
import org.opencdmp.data.tenant.TenantScopedBaseEntity;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.annotation.RequestScope;
|
||||
|
||||
import javax.management.InvalidApplicationException;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
@ -28,11 +27,11 @@ public class TenantScope {
|
|||
}
|
||||
|
||||
public Boolean isMultitenant() {
|
||||
return multitenancy.isMultitenant();
|
||||
return this.multitenancy.isMultitenant();
|
||||
}
|
||||
|
||||
public String getDefaultTenantCode() {
|
||||
return multitenancy.getDefaultTenantCode();
|
||||
return this.multitenancy.getDefaultTenantCode();
|
||||
}
|
||||
|
||||
public Boolean isSet() {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package org.opencdmp.model.deleter;
|
||||
|
||||
import org.opencdmp.data.TenantEntityManager;
|
||||
import org.opencdmp.data.UserCredentialEntity;
|
||||
import org.opencdmp.query.UserCredentialQuery;
|
||||
import gr.cite.tools.data.deleter.Deleter;
|
||||
import gr.cite.tools.data.query.QueryFactory;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
import gr.cite.tools.logging.MapLogEntry;
|
||||
import org.opencdmp.data.TenantEntityManager;
|
||||
import org.opencdmp.data.UserCredentialEntity;
|
||||
import org.opencdmp.query.UserCredentialQuery;
|
||||
import org.opencdmp.service.keycloak.KeycloakService;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
|
@ -19,22 +20,24 @@ import java.util.Optional;
|
|||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public class UserCredentialDeleter implements Deleter {
|
||||
|
||||
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class));
|
||||
private final TenantEntityManager entityManager;
|
||||
|
||||
protected final QueryFactory queryFactory;
|
||||
private final KeycloakService keycloakService;
|
||||
|
||||
|
||||
@Autowired
|
||||
public UserCredentialDeleter(
|
||||
TenantEntityManager entityManager,
|
||||
QueryFactory queryFactory
|
||||
TenantEntityManager entityManager,
|
||||
QueryFactory queryFactory, KeycloakService keycloakService
|
||||
) {
|
||||
this.entityManager = entityManager;
|
||||
this.queryFactory = queryFactory;
|
||||
this.keycloakService = keycloakService;
|
||||
}
|
||||
|
||||
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {
|
||||
|
@ -62,6 +65,8 @@ public class UserCredentialDeleter implements Deleter {
|
|||
logger.trace("deleting item");
|
||||
this.entityManager.remove(item);
|
||||
logger.trace("deleted item");
|
||||
|
||||
this.keycloakService.removeFromAllGroups(item.getExternalId());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.opencdmp.model.persist.actionconfirmation;
|
||||
|
||||
import org.opencdmp.commons.validation.BaseValidator;
|
||||
import gr.cite.tools.validation.specification.Specification;
|
||||
import org.opencdmp.commons.validation.BaseValidator;
|
||||
import org.opencdmp.convention.ConventionService;
|
||||
import org.opencdmp.errorcode.ErrorThesaurusProperties;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
|
@ -20,7 +20,7 @@ public class RemoveCredentialRequestPersist {
|
|||
public static final String _credentialId = "credentialId";
|
||||
|
||||
public UUID getCredentialId() {
|
||||
return credentialId;
|
||||
return this.credentialId;
|
||||
}
|
||||
|
||||
public void setCredentialId(UUID credentialId) {
|
||||
|
@ -51,7 +51,7 @@ public class RemoveCredentialRequestPersist {
|
|||
return Arrays.asList(
|
||||
this.spec()
|
||||
.must(() -> this.isValidGuid(item.getCredentialId()))
|
||||
.failOn(RemoveCredentialRequestPersist._credentialId).failWith(messageSource.getMessage("Validation_Required", new Object[]{RemoveCredentialRequestPersist._credentialId}, LocaleContextHolder.getLocale()))
|
||||
.failOn(RemoveCredentialRequestPersist._credentialId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{RemoveCredentialRequestPersist._credentialId}, LocaleContextHolder.getLocale()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package org.opencdmp.service.keycloak;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.List;
|
||||
|
||||
public interface KeycloakService {
|
||||
|
||||
|
@ -12,6 +10,10 @@ public interface KeycloakService {
|
|||
|
||||
void removeUserFromGroup(@NotNull String subjectId, String groupId);
|
||||
|
||||
List<String> getUserGroups(String subjectId);
|
||||
|
||||
void removeFromAllGroups(String subjectId);
|
||||
|
||||
void addUserToGlobalRoleGroup(String subjectId, String role);
|
||||
void removeUserGlobalRoleGroup(@NotNull String subjectId, String role);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.opencdmp.convention.ConventionService;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -34,6 +35,22 @@ public class KeycloakServiceImpl implements KeycloakService {
|
|||
this.api.users().removeUserFromGroup(subjectId, groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserGroups(String subjectId) {
|
||||
if (this.configuration.getProperties().getAuthorities() == null) return new ArrayList<>();
|
||||
List<GroupRepresentation> group = this.api.users().getGroups(subjectId);
|
||||
if (group != null) return group.stream().map(GroupRepresentation::getId).toList();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromAllGroups(String subjectId){
|
||||
List<String> existingGroups = this.getUserGroups(subjectId);
|
||||
for (String existingGroup : existingGroups){
|
||||
this.removeUserFromGroup(subjectId, existingGroup);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUserToGlobalRoleGroup(String subjectId, String role) {
|
||||
if (this.configuration.getProperties().getAuthorities() == null) return;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package org.opencdmp.service.user;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.opencdmp.model.user.User;
|
||||
import org.opencdmp.model.persist.actionconfirmation.RemoveCredentialRequestPersist;
|
||||
import org.opencdmp.model.persist.UserMergeRequestPersist;
|
||||
import org.opencdmp.model.persist.UserPersist;
|
||||
import org.opencdmp.model.persist.UserRolePatchPersist;
|
||||
import gr.cite.tools.exception.MyApplicationException;
|
||||
import gr.cite.tools.exception.MyForbiddenException;
|
||||
import gr.cite.tools.exception.MyNotFoundException;
|
||||
import gr.cite.tools.exception.MyValidationException;
|
||||
import gr.cite.tools.fieldset.FieldSet;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import org.opencdmp.model.persist.UserMergeRequestPersist;
|
||||
import org.opencdmp.model.persist.UserPersist;
|
||||
import org.opencdmp.model.persist.UserRolePatchPersist;
|
||||
import org.opencdmp.model.persist.actionconfirmation.RemoveCredentialRequestPersist;
|
||||
import org.opencdmp.model.user.User;
|
||||
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.io.IOException;
|
||||
|
@ -40,5 +40,4 @@ public interface UserService {
|
|||
void confirmMergeAccount(String token) throws InvalidApplicationException, IOException;
|
||||
|
||||
void confirmRemoveCredential(String token) throws InvalidApplicationException;
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.opencdmp.commons.types.user.AdditionalInfoEntity;
|
|||
import org.opencdmp.commons.types.usercredential.UserCredentialDataEntity;
|
||||
import org.opencdmp.convention.ConventionService;
|
||||
import org.opencdmp.data.*;
|
||||
import org.opencdmp.data.tenant.TenantScopedBaseEntity;
|
||||
import org.opencdmp.errorcode.ErrorThesaurusProperties;
|
||||
import org.opencdmp.event.EventBroker;
|
||||
import org.opencdmp.event.UserTouchedEvent;
|
||||
|
@ -74,6 +75,7 @@ import java.io.PrintWriter;
|
|||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -100,14 +102,11 @@ public class UserServiceImpl implements UserService {
|
|||
private final XmlHandlingService xmlHandlingService;
|
||||
private final QueryFactory queryFactory;
|
||||
private final UserScope userScope;
|
||||
|
||||
private final KeycloakService keycloakService;
|
||||
private final ActionConfirmationService actionConfirmationService;
|
||||
private final NotificationProperties notificationProperties;
|
||||
private final NotifyIntegrationEventHandler eventHandler;
|
||||
|
||||
private final ValidatorFactory validatorFactory;
|
||||
|
||||
private final ElasticService elasticService;
|
||||
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
|
||||
private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler;
|
||||
|
@ -193,7 +192,7 @@ public class UserServiceImpl implements UserService {
|
|||
if (persist == null) return data;
|
||||
if (persist.getOrganization() != null) {
|
||||
ReferenceEntity organization = this.buildReferenceEntity(persist.getOrganization());
|
||||
data.setOrganizationId(organization != null ? organization.getId() : null);
|
||||
data.setOrganizationId(organization.getId());
|
||||
}
|
||||
data.setRoleOrganization(persist.getRoleOrganization());
|
||||
data.setCulture(persist.getCulture());
|
||||
|
@ -205,7 +204,7 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
private @NotNull ReferenceEntity buildReferenceEntity(ReferencePersist model) throws InvalidApplicationException {
|
||||
|
||||
ReferenceEntity referenceEntity = null;
|
||||
ReferenceEntity referenceEntity;
|
||||
if (this.conventionService.isValidGuid(model.getId())) {
|
||||
referenceEntity = this.entityManager.find(ReferenceEntity.class, model.getId());
|
||||
if (referenceEntity == null)
|
||||
|
@ -333,6 +332,8 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
|
||||
|
||||
this.syncKeycloakRoles(data.getId());
|
||||
|
||||
this.userTouchedIntegrationEventHandler.handle(data.getId());
|
||||
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, User._id), data);
|
||||
}
|
||||
|
@ -352,7 +353,6 @@ public class UserServiceImpl implements UserService {
|
|||
item.setRole(roleName);
|
||||
item.setCreatedAt(Instant.now());
|
||||
this.entityManager.persist(item);
|
||||
this.keycloakService.addUserToGlobalRoleGroup(subjectId, roleName);
|
||||
}
|
||||
foundIds.add(item.getId());
|
||||
}
|
||||
|
@ -360,7 +360,6 @@ public class UserServiceImpl implements UserService {
|
|||
this.entityManager.flush();
|
||||
|
||||
List<UserRoleEntity> toDelete = existingItems.stream().filter(x -> foundIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList());
|
||||
toDelete.forEach(x -> this.keycloakService.removeUserGlobalRoleGroup(subjectId, x.getRole()));
|
||||
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
|
||||
|
||||
this.entityManager.flush();
|
||||
|
@ -388,7 +387,6 @@ public class UserServiceImpl implements UserService {
|
|||
item.setCreatedAt(Instant.now());
|
||||
item.setTenantId(this.tenantScope.getTenant());
|
||||
this.entityManager.persist(item);
|
||||
this.keycloakService.addUserToTenantRoleGroup(subjectId, this.tenantScope.getTenantCode(), roleName);
|
||||
}
|
||||
foundIds.add(item.getId());
|
||||
}
|
||||
|
@ -396,13 +394,6 @@ public class UserServiceImpl implements UserService {
|
|||
this.entityManager.flush();
|
||||
|
||||
List<UserRoleEntity> toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
|
||||
toDelete.forEach(x -> {
|
||||
try {
|
||||
this.keycloakService.removeUserTenantRoleGroup(subjectId, this.tenantScope.getTenantCode(), x.getRole());
|
||||
} catch (InvalidApplicationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
|
||||
|
||||
this.entityManager.flush();
|
||||
|
@ -506,7 +497,6 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
if (this.userScope.getUserIdSafe() == null) throw new MyForbiddenException(this.errors.getForbidden().getCode(), this.errors.getForbidden().getMessage());
|
||||
|
||||
|
||||
String token = this.createMergeAccountConfirmation(model.getEmail());
|
||||
this.createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.EMAIL);
|
||||
}
|
||||
|
@ -571,8 +561,6 @@ public class UserServiceImpl implements UserService {
|
|||
return persist.getToken();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String createRemoveConfirmation(UUID credentialId) throws JAXBException, InvalidApplicationException {
|
||||
ActionConfirmationPersist persist = new ActionConfirmationPersist();
|
||||
persist.setType(ActionConfirmationType.RemoveCredential);
|
||||
|
@ -602,7 +590,7 @@ public class UserServiceImpl implements UserService {
|
|||
this.checkActionState(action);
|
||||
|
||||
MergeAccountConfirmationEntity mergeAccountConfirmationEntity = this.xmlHandlingService.fromXmlSafe(MergeAccountConfirmationEntity.class, action.getData());
|
||||
if (mergeAccountConfirmationEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{mergeAccountConfirmationEntity, MergeAccountConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
if (mergeAccountConfirmationEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{action.getId(), MergeAccountConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
UserContactInfoEntity userContactInfoEntity = this.queryFactory.query(UserContactInfoQuery.class).values(mergeAccountConfirmationEntity.getEmail()).types(ContactInfoType.Email).first();
|
||||
if (userContactInfoEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{mergeAccountConfirmationEntity.getEmail(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
@ -627,6 +615,29 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
this.userTouchedIntegrationEventHandler.handle(newUser.getId());
|
||||
this.userRemovalIntegrationEventHandler.handle(userToBeMerge.getId());
|
||||
|
||||
if (!newUser.getId().equals(userToBeMerge.getId())) {
|
||||
this.syncKeycloakRoles(newUser.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void syncKeycloakRoles(UUID userId){
|
||||
List<UserCredentialEntity> userCredentials = this.queryFactory.query(UserCredentialQuery.class).disableTracking().userIds(userId).collect();
|
||||
List<UserRoleEntity> userRoles = this.queryFactory.query(UserRoleQuery.class).disableTracking().userIds(userId).collect();
|
||||
List<TenantEntity> tenants = this.queryFactory.query(TenantQuery.class).disableTracking().ids(userRoles.stream().map(TenantScopedBaseEntity::getTenantId).filter(Objects::nonNull).toList()).collect();
|
||||
|
||||
for (UserCredentialEntity userCredential : userCredentials){
|
||||
this.keycloakService.removeFromAllGroups(userCredential.getExternalId());
|
||||
for (UserRoleEntity userRole : userRoles) {
|
||||
if (this.authorizationProperties.getAllowedGlobalRoles().contains(userRole.getRole())){
|
||||
this.keycloakService.addUserToGlobalRoleGroup(userCredential.getExternalId(), userRole.getRole());
|
||||
} else if (this.authorizationProperties.getAllowedTenantRoles().contains(userRole.getRole())){
|
||||
String tenantCode = userRole.getTenantId() == null ? this.tenantScope.getDefaultTenantCode() : tenants.stream().filter(x-> x.getId().equals(userRole.getTenantId())).map(TenantEntity::getCode).findFirst().orElse(null);
|
||||
if (!this.conventionService.isNullOrEmpty(tenantCode)) this.keycloakService.addUserToTenantRoleGroup(userCredential.getExternalId(), tenantCode, userRole.getRole());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeNewUserToOld(UserEntity newUser, UserEntity oldUser) throws IOException, InvalidApplicationException {
|
||||
|
@ -731,8 +742,6 @@ public class UserServiceImpl implements UserService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void confirmRemoveCredential(String token) throws InvalidApplicationException {
|
||||
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.RemoveCredential).isActive(IsActive.Active).first();
|
||||
if (action == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{token, ActionConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
@ -740,12 +749,12 @@ public class UserServiceImpl implements UserService {
|
|||
this.checkActionState(action);
|
||||
|
||||
RemoveCredentialRequestEntity removeCredentialRequestEntity = this.xmlHandlingService.fromXmlSafe(RemoveCredentialRequestEntity.class, action.getData());
|
||||
if (removeCredentialRequestEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{removeCredentialRequestEntity, RemoveCredentialRequestEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
if (removeCredentialRequestEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{action.getId(), RemoveCredentialRequestEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
UserCredentialEntity userCredentialEntity = this.queryFactory.query(UserCredentialQuery.class).ids(removeCredentialRequestEntity.getCredentialId()).first();
|
||||
if (userCredentialEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{removeCredentialRequestEntity.getCredentialId(), UserCredential.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
if (!this.userScope.getUserIdSafe().equals(userCredentialEntity.getId())) throw new MyForbiddenException("Only requested user can approve");
|
||||
if (!this.userScope.getUserIdSafe().equals(userCredentialEntity.getUserId())) throw new MyForbiddenException("Only requested user can approve");
|
||||
|
||||
if (userCredentialEntity.getData() != null){
|
||||
UserCredentialDataEntity userCredentialDataEntity = this.jsonHandlingService.fromJsonSafe(UserCredentialDataEntity.class, userCredentialEntity.getData());
|
||||
|
@ -763,9 +772,17 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
this.entityManager.flush();
|
||||
|
||||
this.keycloakService.removeFromAllGroups(userCredentialEntity.getExternalId());
|
||||
this.addToDefaultUserGroups(userCredentialEntity.getExternalId());
|
||||
|
||||
this.userTouchedIntegrationEventHandler.handle(userCredentialEntity.getUserId());
|
||||
}
|
||||
|
||||
private void addToDefaultUserGroups(String subjectId){
|
||||
this.keycloakService.addUserToGlobalRoleGroup(subjectId, this.authorizationProperties.getGlobalUserRole());
|
||||
this.keycloakService.addUserToTenantRoleGroup(subjectId, this.tenantScope.getDefaultTenantCode(), this.authorizationProperties.getTenantUserRole());
|
||||
}
|
||||
|
||||
private void checkActionState(ActionConfirmationEntity action) throws MyApplicationException {
|
||||
if (action.getStatus().equals(ActionConfirmationStatus.Accepted)){
|
||||
throw new MyApplicationException("Account is already confirmed!");
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
authorization:
|
||||
globalAdminRole: Admin
|
||||
tenantAdminRole: TenantAdmin
|
||||
globalUserRole: User
|
||||
tenantUserRole: TenantUser
|
||||
autoAssignGlobalAdminToNewTenants: true
|
||||
allowedTenantRoles:
|
||||
- TenantAdmin
|
||||
|
|
|
@ -125,6 +125,14 @@ export class UserService {
|
|||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
confirmRemoveCredentialAccount(token: Guid): Observable<boolean> {
|
||||
const url = `${this.apiBase}/mine/confirm-remove-credential/token/${token}`;
|
||||
|
||||
return this.http
|
||||
.get<boolean>(url).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
//
|
||||
// Autocomplete Commons
|
||||
//
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { MatDialog } from "@angular/material/dialog";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { AuthService } from "@app/core/services/auth/auth.service";
|
||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||
import { UserService } from "@app/core/services/user/user.service";
|
||||
import { PopupNotificationDialogComponent } from "@app/library/notification/popup/popup-notification.component";
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { Guid } from "@common/types/guid";
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
@ -19,18 +15,12 @@ import { takeUntil } from "rxjs/operators";
|
|||
export class MergeEmailConfirmation extends BaseComponent implements OnInit {
|
||||
|
||||
private token: Guid;
|
||||
public emailFormControl = new UntypedFormControl('');
|
||||
public mailSent: boolean = false;
|
||||
|
||||
|
||||
get showForm(): boolean {
|
||||
return this.token != null;
|
||||
}
|
||||
|
||||
constructor(
|
||||
//TODO: refactor
|
||||
// private emailConfirmationService: MergeEmailConfirmationService,
|
||||
// private authService: AuthService,
|
||||
private userService: UserService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
|
@ -45,30 +35,12 @@ export class MergeEmailConfirmation extends BaseComponent implements OnInit {
|
|||
const token = params['token']
|
||||
if (token != null) {
|
||||
this.token = token;
|
||||
// this.showForm = false;
|
||||
//TODO: refactor
|
||||
// this.emailConfirmationService.emailConfirmation(token)
|
||||
// .pipe(takeUntil(this._destroyed))
|
||||
// .subscribe(
|
||||
// result => {
|
||||
// const email = this.authService.getUserProfileEmail();
|
||||
// if(!email || !result || (email == result))
|
||||
// this.authService.clear();
|
||||
// this.uiNotificationService.snackBarNotification(this.language.instant('USER-PROFILE.MERGING-SUCCESS'), SnackBarNotificationLevel.Success);
|
||||
// this.onCallbackEmailConfirmationSuccess();
|
||||
// },
|
||||
// error => this.onCallbackError(error)
|
||||
// )
|
||||
} else {
|
||||
// this.showForm = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onConfirm(): void {
|
||||
console.log('onConfirm');
|
||||
if (this.showForm === false) return;
|
||||
console.log('active');
|
||||
|
||||
this.userService.confirmMergeAccount(this.token)
|
||||
.subscribe(result => {
|
||||
|
|
|
@ -1 +1,28 @@
|
|||
|
||||
<div class="unlink-account">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col unlink-account-title">{{'UNLINK-ACCOUNT.TITLE' | translate}}</div>
|
||||
</div>
|
||||
<div *ngIf="showForm" class="row unlink-account-content">
|
||||
<div class="col">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
{{ 'UNLINK-ACCOUNT.MESSAGES.CONFIRMATION' | translate }}
|
||||
</span>
|
||||
<!-- <span>Duo ea clita doming eu stet. Nonummy voluptua accusam sit eos aliquyam sit kasd lorem ut feugait no et soluta invidunt ea sanctus. Ut erat molestie sit sit diam dolores lorem nonumy quis consetetur. Elitr sed euismod illum sit consetetur esse at eum elitr sit dolores ut facer. Autem in aliquyam magna eos dolore eos amet ut magna sadipscing sea eum. Justo elitr aliquip praesent est exerci dolore commodo accusam dolor hendrerit rebum feugiat aliquyam sadipscing sed. Dolores dolore autem in et dolor at adipiscing ullamcorper invidunt vel. Et takimata ea amet et at sit kasd erat magna sed.</span> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-auto mt-4">
|
||||
<!-- -->
|
||||
<button type="button" class="normal-btn" (click)="onConfirm()">{{ 'UNLINK-ACCOUNT.ACTIONS.CONFIRM' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #loading>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
.unlink-account {
|
||||
height: fit-content;
|
||||
//margin-top: 80px;
|
||||
min-height: 100vh;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.unlink-account-title {
|
||||
font-size: 1.25rem;
|
||||
color: #212121;
|
||||
padding-top: 4.1875rem;
|
||||
padding-left: 3.75rem;
|
||||
padding-bottom: 4.0625rem;
|
||||
}
|
||||
|
||||
.unlink-account-content {
|
||||
margin-left: 9rem;
|
||||
margin-right: 11rem;
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||
import { UserService } from '@app/core/services/user/user.service';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { takeUntil } from "rxjs/operators";
|
||||
|
||||
|
@ -10,43 +12,57 @@ import { takeUntil } from "rxjs/operators";
|
|||
templateUrl: './unlink-email-confirmation.component.html'
|
||||
})
|
||||
export class UnlinkEmailConfirmation extends BaseComponent implements OnInit {
|
||||
private token: Guid;
|
||||
|
||||
constructor(
|
||||
|
||||
get showForm(): boolean {
|
||||
return this.token != null;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private language: TranslateService,
|
||||
private uiNotificationService: UiNotificationService
|
||||
private uiNotificationService: UiNotificationService,
|
||||
) { super(); }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params
|
||||
ngOnInit() {
|
||||
this.route.params
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(params => {
|
||||
const token = params['token']
|
||||
if (token != null) {
|
||||
//TODO refactor
|
||||
// this.emailConfirmationService.emailConfirmation(token)
|
||||
// .pipe(takeUntil(this._destroyed))
|
||||
// .subscribe(
|
||||
// result => this.onCallbackEmailConfirmationSuccess(),
|
||||
// error => this.onCallbackError(error)
|
||||
// )
|
||||
this.token = token;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onCallbackEmailConfirmationSuccess() {
|
||||
this.router.navigate(['home']);
|
||||
onConfirm(): void {
|
||||
if (this.showForm === false) return;
|
||||
|
||||
this.userService.confirmRemoveCredentialAccount(this.token)
|
||||
.subscribe(result => {
|
||||
if (result) {
|
||||
this.onCallbackConfirmationSuccess();
|
||||
}
|
||||
},
|
||||
error => this.onCallbackError(error));
|
||||
}
|
||||
|
||||
onCallbackConfirmationSuccess() {
|
||||
this.router.navigate(['home'])
|
||||
.then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
onCallbackError(error: any) {
|
||||
if (error.status === 302) {
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('EMAIL-CONFIRMATION.EMAIL-FOUND'), SnackBarNotificationLevel.Warning);
|
||||
this.router.navigate(['home']);
|
||||
}
|
||||
else {
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'), SnackBarNotificationLevel.Error);
|
||||
this.router.navigate(['login']);
|
||||
} else {
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'), SnackBarNotificationLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -160,6 +160,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
|
|||
nameof<User>(x => x.createdAt),
|
||||
nameof<User>(x => x.updatedAt),
|
||||
nameof<User>(x => x.hash),
|
||||
`${nameof<User>(x => x.credentials)}.${nameof<UserCredential>(x => x.id)}`,
|
||||
`${nameof<User>(x => x.credentials)}.${nameof<UserCredential>(x => x.data.email)}`,
|
||||
`${nameof<User>(x => x.credentials)}.${nameof<UserCredential>(x => x.data.externalProviderNames)}`,
|
||||
]
|
||||
|
@ -326,28 +327,15 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
|
|||
.subscribe(confirm => {
|
||||
if (confirm) {
|
||||
|
||||
this.userService.removeCredentialAccount({ credentialId: userCredential.user.id }).subscribe(result => {
|
||||
//TODO
|
||||
this.userService.removeCredentialAccount({ credentialId: userCredential.id }).subscribe(result => {
|
||||
this.dialog.open(PopupNotificationDialogComponent, {
|
||||
data: {
|
||||
title: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT.TITLE'),
|
||||
message: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT.MESSAGE', {'accountToBeUnlinked': userCredential.data?.email})
|
||||
}, maxWidth: '30em'
|
||||
});
|
||||
},
|
||||
error => this.onCallbackError(error));
|
||||
|
||||
//TODO: refactor
|
||||
// const unlinkAccountModel: UnlinkAccountRequestModel = {
|
||||
// userId: this.currentUserId,
|
||||
// email: userCredential.email,
|
||||
// provider: userCredential.provider
|
||||
// };
|
||||
// this.unlinkAccountEmailConfirmation.sendConfirmationEmail(unlinkAccountModel).pipe(takeUntil(this._destroyed)).subscribe(
|
||||
// result => {
|
||||
// this.dialog.open(PopupNotificationDialogComponent, {
|
||||
// data: {
|
||||
// title: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT.TITLE'),
|
||||
// message: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT.MESSAGE', { 'accountToBeUnlinked': userCredential.email })
|
||||
// }, maxWidth: '35em'
|
||||
// });
|
||||
// },
|
||||
// error => { this.onCallbackError(error); }
|
||||
// );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,9 +4,6 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod
|
|||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||
import { FormValidationErrorsDialogModule } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.module';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { LoginComponent } from '../auth/login/login.component';
|
||||
import { LoginModule } from '../auth/login/login.module';
|
||||
import { AddAccountDialogComponent } from './add-account/add-account-dialog.component';
|
||||
import { AddAccountDialogModule } from './add-account/add-account-dialog.module';
|
||||
import { UserProfileComponent } from './user-profile.component';
|
||||
import { UserProfileRoutingModule } from './user-profile.routing';
|
||||
|
|
|
@ -2152,5 +2152,14 @@
|
|||
"ACTIONS": {
|
||||
"CONFIRM": "Confirm"
|
||||
}
|
||||
},
|
||||
"UNLINK-ACCOUNT": {
|
||||
"TITLE": "Unlink Your Account",
|
||||
"MESSAGES": {
|
||||
"CONFIRMATION": "Are you sure that you want to unlink this account?"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CONFIRM": "Confirm"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@
|
|||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a href="{installation-url}/merge/confirmation/{confirmationToken}" target="_blank">Confirm Merge Request</a> </td>
|
||||
<td> <a href="{installation-url}/login/merge/confirmation/{confirmationToken}" target="_blank">Confirm Merge Request</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
<h2>User {userName} have sent you a merge Request.</h2>
|
||||
<p>Please confirm that you want to merge your {installation-url} account with that account.
|
||||
<br/>The link will expire in {expiration_time}.</p>
|
||||
<a href="{installation-url}/merge/confirmation/{confirmationToken}" target="_blank">Confirm Merge Request</a>
|
||||
<a href="{installation-url}/login/merge/confirmation/{confirmationToken}" target="_blank">Confirm Merge Request</a>
|
||||
</body>
|
||||
</html>
|
|
@ -271,7 +271,7 @@
|
|||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a href="{installation-url}/unlink/confirmation/{confirmationToken}" target="_blank">Confirm Unlink Request</a> </td>
|
||||
<td> <a href="{installation-url}/login/unlink/confirmation/{confirmationToken}" target="_blank">Confirm Unlink Request</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
<h2>You have made a request to unlink your email account in ARGOS.</h2>
|
||||
<p>Please confirm that you want to unlink your {email} account.
|
||||
<br/>The link will expire in {expiration_time}.</p>
|
||||
<a href="{installation-url}/unlink/confirmation/{confirmationToken}" target="_blank">Confirm Unlink Request</a>
|
||||
<a href="{installation-url}/login/unlink/confirmation/{confirmationToken}" target="_blank">Confirm Unlink Request</a>
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +1,15 @@
|
|||
package gr.cite.notification.integrationevent.inbox.notify;
|
||||
|
||||
import gr.cite.notification.common.StringUtils;
|
||||
import gr.cite.notification.common.enums.ContactInfoType;
|
||||
import gr.cite.notification.common.enums.IsActive;
|
||||
import gr.cite.notification.common.enums.NotificationContactType;
|
||||
import gr.cite.notification.data.UserContactInfoEntity;
|
||||
import gr.cite.notification.integrationevent.inbox.ConsistencyHandler;
|
||||
import gr.cite.notification.model.User;
|
||||
import gr.cite.notification.model.UserContactInfo;
|
||||
import gr.cite.notification.model.builder.UserBuilder;
|
||||
import gr.cite.notification.query.UserContactInfoQuery;
|
||||
import gr.cite.notification.query.UserQuery;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.query.QueryFactory;
|
||||
|
@ -39,9 +43,10 @@ public class NotifyConsistencyHandler implements ConsistencyHandler<NotifyConsis
|
|||
User user = this.builderFactory.builder(UserBuilder.class).build(fieldSet, query.firstAs(fieldSet));
|
||||
if (user == null)
|
||||
return false;
|
||||
return consistencyPredicates.getContactTypeHint() == null || consistencyPredicates.getContactTypeHint() == NotificationContactType.IN_APP || !StringUtils.isNullOrEmpty(consistencyPredicates.getContactHint());
|
||||
Long activeEmails = this.queryFactory.query(UserContactInfoQuery.class).ids(consistencyPredicates.getUserId()).isActive(IsActive.Active).type(ContactInfoType.Email).count();
|
||||
return consistencyPredicates.getContactTypeHint() == null || consistencyPredicates.getContactTypeHint() == NotificationContactType.IN_APP || !StringUtils.isNullOrEmpty(consistencyPredicates.getContactHint()) || activeEmails != 0;
|
||||
} else {
|
||||
return !StringUtils.isNullOrEmpty(consistencyPredicates.getContactHint());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue