unlink merge fixes

This commit is contained in:
Efstratios Giannopoulos 2024-05-23 15:00:11 +03:00
parent 62120517b1
commit 9e1cf8f642
24 changed files with 258 additions and 160 deletions

View File

@ -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;
}
}

View File

@ -15,6 +15,6 @@ public class LocaleConfiguration {
}
public LocaleProperties getProperties() {
return properties;
return this.properties;
}
}

View File

@ -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) {

View File

@ -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() {

View File

@ -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());
}
}

View File

@ -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()))
);
}
}

View File

@ -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);

View File

@ -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;
@ -33,7 +34,23 @@ public class KeycloakServiceImpl implements KeycloakService {
public void removeUserFromGroup(@NotNull String subjectId, String groupId) {
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;

View File

@ -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;
}

View File

@ -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,8 +772,16 @@ 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)){

View File

@ -1,6 +1,8 @@
authorization:
globalAdminRole: Admin
tenantAdminRole: TenantAdmin
globalUserRole: User
tenantUserRole: TenantUser
autoAssignGlobalAdminToNewTenants: true
allowedTenantRoles:
- TenantAdmin

View File

@ -106,11 +106,11 @@ export class UserService {
.post<boolean>(url, item).pipe(
catchError((error: any) => throwError(error)));
}
removeCredentialAccount(item: RemoveCredentialRequestPersist): Observable<boolean> {
const url = `${this.apiBase}/mine/remove-credential-request`;
console.log(item);
console.log(url);
console.log(item);
console.log(url);
return this.http
.post<boolean>(url, item).pipe(
@ -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
//

View File

@ -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';
@ -17,20 +13,14 @@ import { takeUntil } from "rxjs/operators";
styleUrls: ['./merge-email-confirmation.component.scss']
})
export class MergeEmailConfirmation extends BaseComponent implements OnInit {
private token: Guid;
public emailFormControl = new UntypedFormControl('');
public mailSent: boolean = false;
private token: Guid;
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 => {

View File

@ -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>

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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); }
// );
}
});
}

View File

@ -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';

View File

@ -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"
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}