fix tenant select

This commit is contained in:
Efstratios Giannopoulos 2024-05-17 11:27:33 +03:00
parent a9468abe61
commit 30e5ed504a
11 changed files with 96 additions and 52 deletions

View File

@ -1,18 +1,16 @@
package org.opencdmp.service.tenant; package org.opencdmp.service.tenant;
import org.opencdmp.model.Tenant;
import org.opencdmp.model.persist.TenantPersist;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
import org.opencdmp.model.Tenant;
import org.opencdmp.model.persist.TenantPersist;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
@ -26,4 +24,6 @@ public interface TenantService {
InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException;
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
List<Tenant> myTenants(FieldSet fieldSet) throws MyForbiddenException;
} }

View File

@ -1,8 +1,12 @@
package org.opencdmp.service.tenant; package org.opencdmp.service.tenant;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.Ordering;
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.MyForbiddenException;
@ -14,6 +18,7 @@ import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.AuthorizationProperties; import org.opencdmp.authorization.AuthorizationProperties;
import org.opencdmp.authorization.ClaimNames;
import org.opencdmp.authorization.Permission; import org.opencdmp.authorization.Permission;
import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.tenant.TenantScope;
@ -31,6 +36,7 @@ import org.opencdmp.model.Tenant;
import org.opencdmp.model.builder.TenantBuilder; import org.opencdmp.model.builder.TenantBuilder;
import org.opencdmp.model.deleter.TenantDeleter; import org.opencdmp.model.deleter.TenantDeleter;
import org.opencdmp.model.persist.TenantPersist; import org.opencdmp.model.persist.TenantPersist;
import org.opencdmp.query.TenantQuery;
import org.opencdmp.query.UserCredentialQuery; import org.opencdmp.query.UserCredentialQuery;
import org.opencdmp.query.UserRoleQuery; import org.opencdmp.query.UserRoleQuery;
import org.opencdmp.service.keycloak.KeycloakService; import org.opencdmp.service.keycloak.KeycloakService;
@ -48,6 +54,7 @@ import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -73,6 +80,8 @@ public class TenantServiceImpl implements TenantService {
private final AuthorizationProperties authorizationProperties; private final AuthorizationProperties authorizationProperties;
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final ClaimExtractor claimExtractor;
@Autowired @Autowired
@ -83,7 +92,7 @@ public class TenantServiceImpl implements TenantService {
BuilderFactory builderFactory, BuilderFactory builderFactory,
ConventionService conventionService, ConventionService conventionService,
MessageSource messageSource, MessageSource messageSource,
ErrorThesaurusProperties errors, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory) { ErrorThesaurusProperties errors, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -97,6 +106,8 @@ public class TenantServiceImpl implements TenantService {
this.authorizationProperties = authorizationProperties; this.authorizationProperties = authorizationProperties;
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor;
} }
@Override @Override
@ -177,8 +188,6 @@ public class TenantServiceImpl implements TenantService {
} }
@Override @Override
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting : {}", id); logger.debug("deleting : {}", id);
@ -192,5 +201,38 @@ public class TenantServiceImpl implements TenantService {
this.tenantRemovalIntegrationEventHandler.handle(tenantRemovalIntegrationEvent); this.tenantRemovalIntegrationEventHandler.handle(tenantRemovalIntegrationEvent);
} }
@Override
public List<Tenant> myTenants(FieldSet fieldSet) throws MyForbiddenException {
logger.debug("my tenants");
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();
List<String> tenants = this.claimExtractor.asStrings(principal, ClaimNames.TenantCodesClaimName);
List<Tenant> models = new ArrayList<>();
if (tenants != null && !tenants.isEmpty()) {
if (fieldSet == null || fieldSet.isEmpty()) {
fieldSet = new BaseFieldSet(
Tenant._id,
Tenant._code,
Tenant._name);
}
TenantQuery query = this.queryFactory.query(TenantQuery.class).codes(tenants).isActive(IsActive.Active);
query.setOrder(new Ordering().addAscending(Tenant._name));
List<TenantEntity> data = query.collectAs(fieldSet);
models.addAll(this.builderFactory.builder(TenantBuilder.class).build(fieldSet, data));
if (tenants.contains(this.tenantScope.getDefaultTenantCode())){
Tenant tenant = new Tenant();
tenant.setCode(this.tenantScope.getDefaultTenantCode());
tenant.setName(this.messageSource.getMessage("DefaultTenant_Name", new Object[]{}, LocaleContextHolder.getLocale()));
models.addFirst(tenant);
}
}
return models;
}
} }

View File

@ -1,17 +1,16 @@
package org.opencdmp.controllers; package org.opencdmp.controllers;
import org.opencdmp.audit.AuditableAction;
import org.opencdmp.authorization.ClaimNames;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.models.Account;
import org.opencdmp.models.AccountBuilder;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
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.LoggerService; import gr.cite.tools.logging.LoggerService;
import org.opencdmp.audit.AuditableAction;
import org.opencdmp.model.Tenant;
import org.opencdmp.models.Account;
import org.opencdmp.models.AccountBuilder;
import org.opencdmp.service.tenant.TenantService;
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.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -21,27 +20,26 @@ import org.springframework.web.bind.annotation.RestController;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@RestController @RestController
@RequestMapping(value = { "/api/principal/" }) @RequestMapping("/api/principal/")
public class PrincipalController { public class PrincipalController {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PrincipalController.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PrincipalController.class));
private final AuditService auditService; private final AuditService auditService;
private final CurrentPrincipalResolver currentPrincipalResolver; private final CurrentPrincipalResolver currentPrincipalResolver;
private final AccountBuilder accountBuilder; private final AccountBuilder accountBuilder;
private final ClaimExtractor claimExtractor; private final TenantService tenantService;
@Autowired @Autowired
public PrincipalController( public PrincipalController(
CurrentPrincipalResolver currentPrincipalResolver, CurrentPrincipalResolver currentPrincipalResolver,
AccountBuilder accountBuilder, AccountBuilder accountBuilder,
AuditService auditService, ClaimExtractor claimExtractor) { AuditService auditService, TenantService tenantService) {
this.currentPrincipalResolver = currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
this.accountBuilder = accountBuilder; this.accountBuilder = accountBuilder;
this.auditService = auditService; this.auditService = auditService;
this.claimExtractor = claimExtractor; this.tenantService = tenantService;
} }
@RequestMapping(path = "me", method = RequestMethod.GET ) @RequestMapping(path = "me", method = RequestMethod.GET )
@ -80,16 +78,15 @@ public class PrincipalController {
} }
@GetMapping("my-tenants") @GetMapping("my-tenants")
public List<String> myTenants() { public List<Tenant> myTenants(FieldSet fieldSet) {
logger.debug("my-tenants"); logger.debug("my-tenants");
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); List<Tenant> models = this.tenantService.myTenants(fieldSet);
List<String> tenants = this.claimExtractor.asStrings(principal, ClaimNames.TenantCodesClaimName);
this.auditService.track(AuditableAction.Principal_MyTenants); this.auditService.track(AuditableAction.Principal_MyTenants);
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
return tenants == null ? null : tenants.stream().distinct().collect(Collectors.toList()); return models;
} }
} }

View File

@ -1,10 +1,5 @@
package org.opencdmp.models; package org.opencdmp.models;
import org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.commons.types.user.AdditionalInfoEntity;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.data.UserEntity;
import gr.cite.commons.web.authz.configuration.AuthorizationConfiguration; import gr.cite.commons.web.authz.configuration.AuthorizationConfiguration;
import gr.cite.commons.web.authz.configuration.Permission; import gr.cite.commons.web.authz.configuration.Permission;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
@ -13,6 +8,11 @@ import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorKeys; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorKeys;
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 org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.commons.types.user.AdditionalInfoEntity;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.data.UserEntity;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -21,7 +21,7 @@ import javax.management.InvalidApplicationException;
import java.util.*; import java.util.*;
@Component @Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AccountBuilder { public class AccountBuilder {
private final ClaimExtractor claimExtractor; private final ClaimExtractor claimExtractor;
@ -91,13 +91,13 @@ public class AccountBuilder {
} }
} }
if (fields.hasField(Account._roles)) { if (fields.hasField(Account._roles)) {
List<String> roles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal()); List<String> roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
model.setRoles(roles); model.setRoles(roles);
} }
if (fields.hasField(Account._permissions)) { if (fields.hasField(Account._permissions)) {
List<String> roles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal()); List<String> roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
Set<String> permissions = authorizationConfiguration.permissionsOfRoles(roles); Set<String> permissions = this.authorizationConfiguration.permissionsOfRoles(roles);
for (Map.Entry<String, Permission> permissionEntry : authorizationConfiguration.getRawPolicies().entrySet()){ for (Map.Entry<String, Permission> permissionEntry : this.authorizationConfiguration.getRawPolicies().entrySet()){
if (permissionEntry.getValue().getAllowAuthenticated()){ if (permissionEntry.getValue().getAllowAuthenticated()){
permissions.add(permissionEntry.getKey()); permissions.add(permissionEntry.getKey());
} }

View File

@ -28,3 +28,5 @@ Validation.MissingFields= missing fields: {0}
Validation.InvalidDescriptionTemplateMultiplicity= {0} can not be used Validation.InvalidDescriptionTemplateMultiplicity= {0} can not be used
Validation.InvalidDescriptionTemplateMultiplicityOnDMP= Description Templates has multiplicity errors Validation.InvalidDescriptionTemplateMultiplicityOnDMP= Description Templates has multiplicity errors
Validation_UrlRequired={0} is not valid url Validation_UrlRequired={0} is not valid url
DefaultTenant_Name=Default

View File

@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
import { ConfigurationService } from '../configuration/configuration.service'; import { ConfigurationService } from '../configuration/configuration.service';
import { BaseHttpV2Service } from '../http/base-http-v2.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Tenant } from '@app/core/model/tenant/tenant';
@Injectable() @Injectable()
export class PrincipalService { export class PrincipalService {
@ -20,8 +21,8 @@ export class PrincipalService {
return this.http.get<AppAccount>(url, options); return this.http.get<AppAccount>(url, options);
} }
public myTenants(options?: Object): Observable<Array<string>> { public myTenants(options?: Object): Observable<Array<Tenant>> {
const url = `${this.apiBase}/my-tenants`; const url = `${this.apiBase}/my-tenants`;
return this.http.get<Array<string>>(url, options); return this.http.get<Array<Tenant>>(url, options);
} }
} }

View File

@ -64,9 +64,9 @@ export class PostLoginComponent extends BaseComponent implements OnInit {
this.principalService.myTenants({ params: params }).subscribe(myTenants => { this.principalService.myTenants({ params: params }).subscribe(myTenants => {
if (myTenants) { if (myTenants) {
if (myTenants.length > 1) { if (myTenants.length > 1) {
this.tenants = myTenants.map(function (code) { return { 'code': code }; }); this.tenants = myTenants;
} else if (myTenants.length === 1) { } else if (myTenants.length === 1) {
this.authService.selectedTenant(myTenants[0]); this.authService.selectedTenant(myTenants[0]?.code);
this.loadUser(); this.loadUser();
} else { } else {
this.authService.selectedTenant(null); this.authService.selectedTenant(null);

View File

@ -1,5 +1,5 @@
<mat-button-toggle-group class="tenant-menu" vertical (change)="onTenantSelected($event)" [value]="this.currentTenant"> <mat-button-toggle-group class="tenant-menu" vertical (change)="onTenantSelected($event)" [value]="this.currentTenant">
<div *ngFor="let tenant of tenants | async"> <div *ngFor="let tenant of tenants | async">
<mat-button-toggle class="tenant-button" [value]="tenant">{{ tenant }}</mat-button-toggle> <mat-button-toggle class="tenant-button" [value]="tenant.code">{{ tenant.name }}</mat-button-toggle>
</div> </div>
</mat-button-toggle-group> </mat-button-toggle-group>

View File

@ -1,6 +1,7 @@
import { Component, EventEmitter, OnInit, Output } from "@angular/core"; import { Component, EventEmitter, OnInit, Output } from "@angular/core";
import { MatButtonToggleChange } from "@angular/material/button-toggle"; import { MatButtonToggleChange } from "@angular/material/button-toggle";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { Tenant } from "@app/core/model/tenant/tenant";
import { AuthService } from "@app/core/services/auth/auth.service"; import { AuthService } from "@app/core/services/auth/auth.service";
import { PrincipalService } from "@app/core/services/http/principal.service"; import { PrincipalService } from "@app/core/services/http/principal.service";
import { BaseComponent } from "@common/base/base.component"; import { BaseComponent } from "@common/base/base.component";
@ -16,7 +17,7 @@ import { takeUntil } from "rxjs/operators";
styleUrls: ['tenant-switch.component.scss'] styleUrls: ['tenant-switch.component.scss']
}) })
export class TenantSwitchComponent extends BaseComponent implements OnInit { export class TenantSwitchComponent extends BaseComponent implements OnInit {
tenants: Observable<Array<string>>; tenants: Observable<Array<Tenant>>;
constructor( constructor(
private router: Router, private router: Router,
@ -36,7 +37,7 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit {
//this.tenantChange.emit(this.getCurrentLanguage()) //this.tenantChange.emit(this.getCurrentLanguage())
} }
loadUserTenants(): Observable<Array<string>> { loadUserTenants(): Observable<Array<Tenant>> {
const params = new BaseHttpParams(); const params = new BaseHttpParams();
params.interceptorContext = { params.interceptorContext = {
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor] excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]

View File

@ -15,7 +15,7 @@
<mat-label>Tenant</mat-label> <mat-label>Tenant</mat-label>
<mat-select placeholder="Tenant" [formControl]="this.tenantFormGroup.get('tenantCode')"> <mat-select placeholder="Tenant" [formControl]="this.tenantFormGroup.get('tenantCode')">
<ng-container *ngFor="let tenant of tenants | async"> <ng-container *ngFor="let tenant of tenants | async">
<mat-option [value]="tenant">{{ tenant }}</mat-option> <mat-option [value]="tenant.code">{{ tenant.name }}</mat-option>
</ng-container> </ng-container>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>

View File

@ -38,6 +38,7 @@ import { ReferenceTypeService } from '@app/core/services/reference-type/referenc
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type';
import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { Tenant } from '@app/core/model/tenant/tenant';
@Component({ @Component({
selector: 'app-user-profile', selector: 'app-user-profile',
@ -61,7 +62,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
errorMessages = []; errorMessages = [];
nestedCount = []; nestedCount = [];
nestedIndex = 0; nestedIndex = 0;
tenants: Observable<Array<string>>; tenants: Observable<Array<Tenant>>;
expandedPreferences: boolean = false; expandedPreferences: boolean = false;
organisationsSingleAutoCompleteConfiguration: SingleAutoCompleteConfiguration; organisationsSingleAutoCompleteConfiguration: SingleAutoCompleteConfiguration;
@ -438,7 +439,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
} }
// Switch Tenant // Switch Tenant
loadUserTenants(): Observable<Array<string>> { loadUserTenants(): Observable<Array<Tenant>> {
const params = new BaseHttpParams(); const params = new BaseHttpParams();
params.interceptorContext = { params.interceptorContext = {
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor] excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]