diff --git a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantService.java b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantService.java index 8e39ff6a9..24acbc16f 100644 --- a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantService.java +++ b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantService.java @@ -1,18 +1,16 @@ 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.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; +import org.opencdmp.model.Tenant; +import org.opencdmp.model.persist.TenantPersist; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; import javax.management.InvalidApplicationException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -26,4 +24,6 @@ public interface TenantService { InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; + + List myTenants(FieldSet fieldSet) throws MyForbiddenException; } diff --git a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java index 311f67f5f..67a0205d5 100644 --- a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java @@ -1,8 +1,12 @@ package org.opencdmp.service.tenant; 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.deleter.DeleterFactory; +import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; @@ -14,6 +18,7 @@ import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.AuthorizationProperties; +import org.opencdmp.authorization.ClaimNames; import org.opencdmp.authorization.Permission; import org.opencdmp.commons.enums.IsActive; 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.deleter.TenantDeleter; import org.opencdmp.model.persist.TenantPersist; +import org.opencdmp.query.TenantQuery; import org.opencdmp.query.UserCredentialQuery; import org.opencdmp.query.UserRoleQuery; import org.opencdmp.service.keycloak.KeycloakService; @@ -48,6 +54,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -73,6 +80,8 @@ public class TenantServiceImpl implements TenantService { private final AuthorizationProperties authorizationProperties; private final TenantScope tenantScope; private final QueryFactory queryFactory; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractor claimExtractor; @Autowired @@ -83,7 +92,7 @@ public class TenantServiceImpl implements TenantService { BuilderFactory builderFactory, ConventionService conventionService, 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.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -97,6 +106,8 @@ public class TenantServiceImpl implements TenantService { this.authorizationProperties = authorizationProperties; this.tenantScope = tenantScope; this.queryFactory = queryFactory; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractor = claimExtractor; } @Override @@ -177,8 +188,6 @@ public class TenantServiceImpl implements TenantService { } - - @Override public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting : {}", id); @@ -192,5 +201,38 @@ public class TenantServiceImpl implements TenantService { this.tenantRemovalIntegrationEventHandler.handle(tenantRemovalIntegrationEvent); } + + + @Override + public List myTenants(FieldSet fieldSet) throws MyForbiddenException { + logger.debug("my tenants"); + + MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); + List tenants = this.claimExtractor.asStrings(principal, ClaimNames.TenantCodesClaimName); + List 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 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; + } + } diff --git a/backend/web/src/main/java/org/opencdmp/controllers/PrincipalController.java b/backend/web/src/main/java/org/opencdmp/controllers/PrincipalController.java index 01d05740e..30df186aa 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/PrincipalController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/PrincipalController.java @@ -1,17 +1,16 @@ 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.MyPrincipal; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -21,27 +20,26 @@ import org.springframework.web.bind.annotation.RestController; import javax.management.InvalidApplicationException; import java.util.List; -import java.util.stream.Collectors; @RestController -@RequestMapping(value = { "/api/principal/" }) +@RequestMapping("/api/principal/") public class PrincipalController { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PrincipalController.class)); private final AuditService auditService; private final CurrentPrincipalResolver currentPrincipalResolver; private final AccountBuilder accountBuilder; - private final ClaimExtractor claimExtractor; + private final TenantService tenantService; @Autowired public PrincipalController( CurrentPrincipalResolver currentPrincipalResolver, AccountBuilder accountBuilder, - AuditService auditService, ClaimExtractor claimExtractor) { + AuditService auditService, TenantService tenantService) { this.currentPrincipalResolver = currentPrincipalResolver; this.accountBuilder = accountBuilder; this.auditService = auditService; - this.claimExtractor = claimExtractor; + this.tenantService = tenantService; } @RequestMapping(path = "me", method = RequestMethod.GET ) @@ -80,16 +78,15 @@ public class PrincipalController { } @GetMapping("my-tenants") - public List myTenants() { + public List myTenants(FieldSet fieldSet) { logger.debug("my-tenants"); - MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); - List tenants = this.claimExtractor.asStrings(principal, ClaimNames.TenantCodesClaimName); + List models = this.tenantService.myTenants(fieldSet); this.auditService.track(AuditableAction.Principal_MyTenants); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - return tenants == null ? null : tenants.stream().distinct().collect(Collectors.toList()); + + return models; } } diff --git a/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java b/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java index 1376898b4..2bae2acd5 100644 --- a/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java +++ b/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java @@ -1,10 +1,5 @@ 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.Permission; 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.tools.fieldset.BaseFieldSet; 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.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -21,7 +21,7 @@ import javax.management.InvalidApplicationException; import java.util.*; @Component -@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class AccountBuilder { private final ClaimExtractor claimExtractor; @@ -91,13 +91,13 @@ public class AccountBuilder { } } if (fields.hasField(Account._roles)) { - List roles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal()); + List roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal()); model.setRoles(roles); } if (fields.hasField(Account._permissions)) { - List roles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal()); - Set permissions = authorizationConfiguration.permissionsOfRoles(roles); - for (Map.Entry permissionEntry : authorizationConfiguration.getRawPolicies().entrySet()){ + List roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal()); + Set permissions = this.authorizationConfiguration.permissionsOfRoles(roles); + for (Map.Entry permissionEntry : this.authorizationConfiguration.getRawPolicies().entrySet()){ if (permissionEntry.getValue().getAllowAuthenticated()){ permissions.add(permissionEntry.getKey()); } diff --git a/backend/web/src/main/resources/messages.properties b/backend/web/src/main/resources/messages.properties index d590e2ebc..9c04fd2b4 100644 --- a/backend/web/src/main/resources/messages.properties +++ b/backend/web/src/main/resources/messages.properties @@ -27,4 +27,6 @@ Validation.LargerThenEqual= value {0} must be equal or larger than {1} Validation.MissingFields= missing fields: {0} Validation.InvalidDescriptionTemplateMultiplicity= {0} can not be used Validation.InvalidDescriptionTemplateMultiplicityOnDMP= Description Templates has multiplicity errors -Validation_UrlRequired={0} is not valid url \ No newline at end of file +Validation_UrlRequired={0} is not valid url + +DefaultTenant_Name=Default \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/http/principal.service.ts b/dmp-frontend/src/app/core/services/http/principal.service.ts index fb2d64c65..a5538a027 100644 --- a/dmp-frontend/src/app/core/services/http/principal.service.ts +++ b/dmp-frontend/src/app/core/services/http/principal.service.ts @@ -4,6 +4,7 @@ import { Observable } from 'rxjs'; import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service'; import { map } from 'rxjs/operators'; +import { Tenant } from '@app/core/model/tenant/tenant'; @Injectable() export class PrincipalService { @@ -20,8 +21,8 @@ export class PrincipalService { return this.http.get(url, options); } - public myTenants(options?: Object): Observable> { + public myTenants(options?: Object): Observable> { const url = `${this.apiBase}/my-tenants`; - return this.http.get>(url, options); + return this.http.get>(url, options); } } diff --git a/dmp-frontend/src/app/ui/auth/login/post-login/post-login.component.ts b/dmp-frontend/src/app/ui/auth/login/post-login/post-login.component.ts index 4a0a94b5c..bf72c9cc5 100644 --- a/dmp-frontend/src/app/ui/auth/login/post-login/post-login.component.ts +++ b/dmp-frontend/src/app/ui/auth/login/post-login/post-login.component.ts @@ -64,9 +64,9 @@ export class PostLoginComponent extends BaseComponent implements OnInit { this.principalService.myTenants({ params: params }).subscribe(myTenants => { if (myTenants) { if (myTenants.length > 1) { - this.tenants = myTenants.map(function (code) { return { 'code': code }; }); + this.tenants = myTenants; } else if (myTenants.length === 1) { - this.authService.selectedTenant(myTenants[0]); + this.authService.selectedTenant(myTenants[0]?.code); this.loadUser(); } else { this.authService.selectedTenant(null); diff --git a/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.html b/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.html index 775d85bba..9a36ad797 100644 --- a/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.html +++ b/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.html @@ -1,5 +1,5 @@
- {{ tenant }} + {{ tenant.name }}
diff --git a/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.ts b/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.ts index 1e053cb95..8c28a854a 100644 --- a/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.ts +++ b/dmp-frontend/src/app/ui/tenant/tenant-switch/tenant-switch.component.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, OnInit, Output } from "@angular/core"; import { MatButtonToggleChange } from "@angular/material/button-toggle"; import { Router } from "@angular/router"; +import { Tenant } from "@app/core/model/tenant/tenant"; import { AuthService } from "@app/core/services/auth/auth.service"; import { PrincipalService } from "@app/core/services/http/principal.service"; import { BaseComponent } from "@common/base/base.component"; @@ -16,7 +17,7 @@ import { takeUntil } from "rxjs/operators"; styleUrls: ['tenant-switch.component.scss'] }) export class TenantSwitchComponent extends BaseComponent implements OnInit { - tenants: Observable>; + tenants: Observable>; constructor( private router: Router, @@ -26,7 +27,7 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit { ) { super(); } - + get currentTenant(): string { return this.authService.selectedTenant(); } @@ -36,7 +37,7 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit { //this.tenantChange.emit(this.getCurrentLanguage()) } - loadUserTenants(): Observable> { + loadUserTenants(): Observable> { const params = new BaseHttpParams(); params.interceptorContext = { excludedInterceptors: [InterceptorType.TenantHeaderInterceptor] @@ -61,8 +62,8 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit { .subscribe( () => { this.authService.onAuthenticateSuccessReload(); - }, + }, (error) => this.authService.onAuthenticateError(error) ); } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html index 06de05ffd..19f7600a6 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html @@ -15,7 +15,7 @@ Tenant - {{ tenant }} + {{ tenant.name }} @@ -83,9 +83,9 @@ --> @@ -226,7 +226,7 @@ - + diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts index 5179089e0..8b55e5be2 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts @@ -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 { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; import { ReferenceType } from '@app/core/model/reference-type/reference-type'; +import { Tenant } from '@app/core/model/tenant/tenant'; @Component({ selector: 'app-user-profile', @@ -61,7 +62,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes errorMessages = []; nestedCount = []; nestedIndex = 0; - tenants: Observable>; + tenants: Observable>; expandedPreferences: boolean = false; organisationsSingleAutoCompleteConfiguration: SingleAutoCompleteConfiguration; @@ -154,7 +155,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes [nameof(x => x.additionalInfo), nameof(x => x.organization), nameof(x => x.reference)].join('.'), [nameof(x => x.additionalInfo), nameof(x => x.organization), nameof(x => x.source)].join('.'), [nameof(x => x.additionalInfo), nameof(x => x.organization), nameof(x => x.sourceType)].join('.'), - [nameof(x => x.additionalInfo), nameof(x => x.organization), nameof(x => x.isActive)].join('.'), + [nameof(x => x.additionalInfo), nameof(x => x.organization), nameof(x => x.isActive)].join('.'), nameof(x => x.additionalInfo.roleOrganization), nameof(x => x.createdAt), nameof(x => x.updatedAt), @@ -438,7 +439,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes } // Switch Tenant - loadUserTenants(): Observable> { + loadUserTenants(): Observable> { const params = new BaseHttpParams(); params.interceptorContext = { excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]