This commit is contained in:
Diamantis Tziotzios 2024-06-12 10:43:32 +03:00
parent 740bf41bfa
commit f75428595f
9 changed files with 71 additions and 89 deletions

View File

@ -63,8 +63,10 @@ public class PrincipalController {
BaseFieldSet.asIndexer(Account._profile, Account.UserProfileInfo._language),
BaseFieldSet.asIndexer(Account._profile, Account.UserProfileInfo._culture),
BaseFieldSet.asIndexer(Account._profile, Account.UserProfileInfo._timezone),
Account._roles,
Account._permissions);
Account._permissions,
BaseFieldSet.asIndexer(Account._selectedTenant, Tenant._id),
BaseFieldSet.asIndexer(Account._selectedTenant, Tenant._name),
BaseFieldSet.asIndexer(Account._selectedTenant, Tenant._code));
}
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();

View File

@ -1,6 +1,8 @@
package org.opencdmp.models;
import gr.cite.tools.logging.annotation.LogSensitive;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.model.Tenant;
import java.time.Instant;
import java.util.List;
@ -187,6 +189,9 @@ public class Account {
public final static String _permissions = "permissions";
private List<String> permissions;
public final static String _selectedTenant = "selectedTenant";
public Tenant selectedTenant;
public PrincipalInfo getPrincipal() {
return principal;
}
@ -195,22 +200,10 @@ public class Account {
this.principal = principal;
}
public final static String _roles = "roles";
private List<String> roles;
public Boolean getAuthenticated() {
return isAuthenticated;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public void setAuthenticated(Boolean authenticated) {
isAuthenticated = authenticated;
}
@ -230,4 +223,12 @@ public class Account {
public void setProfile(UserProfileInfo profile) {
this.profile = profile;
}
public Tenant getSelectedTenant() {
return selectedTenant;
}
public void setSelectedTenant(Tenant selectedTenant) {
this.selectedTenant = selectedTenant;
}
}

View File

@ -6,19 +6,30 @@ 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.commons.web.oidc.principal.extractor.ClaimExtractorKeys;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.commons.types.user.AdditionalInfoEntity;
import org.opencdmp.data.DmpEntity;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.data.UserEntity;
import org.opencdmp.model.builder.BaseBuilder;
import org.opencdmp.model.builder.TenantBuilder;
import org.opencdmp.model.builder.dmpreference.DmpReferenceBuilder;
import org.opencdmp.model.dmp.Dmp;
import org.opencdmp.query.DmpReferenceQuery;
import org.opencdmp.query.TenantQuery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@ -31,13 +42,20 @@ public class AccountBuilder {
private final JsonHandlingService jsonHandlingService;
private final UserScope userScope;
private final TenantEntityManager entityManager;
public AccountBuilder(ClaimExtractor claimExtractor, CurrentPrincipalResolver currentPrincipalResolver, AuthorizationConfiguration authorizationConfiguration, JsonHandlingService jsonHandlingService, UserScope userScope, TenantEntityManager entityManager) {
private final TenantScope tenantScope;
private final QueryFactory queryFactory;
private final BuilderFactory builderFactory;
public AccountBuilder(ClaimExtractor claimExtractor, CurrentPrincipalResolver currentPrincipalResolver, AuthorizationConfiguration authorizationConfiguration, JsonHandlingService jsonHandlingService, UserScope userScope, TenantEntityManager entityManager, TenantScope tenantScope, QueryFactory queryFactory, BuilderFactory builderFactory) {
this.claimExtractor = claimExtractor;
this.currentPrincipalResolver = currentPrincipalResolver;
this.authorizationConfiguration = authorizationConfiguration;
this.jsonHandlingService = jsonHandlingService;
this.userScope = userScope;
this.entityManager = entityManager;
this.tenantScope = tenantScope;
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.excludeMoreClaim = Set.of(
ClaimExtractorKeys.Subject,
ClaimExtractorKeys.Name,
@ -90,10 +108,6 @@ public class AccountBuilder {
model.getPrincipal().getMore().get(key).addAll(values);
}
}
if (fields.hasField(Account._roles)) {
List<String> roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
model.setRoles(roles);
}
if (fields.hasField(Account._permissions)) {
List<String> roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
Set<String> permissions = this.authorizationConfiguration.permissionsOfRoles(roles);
@ -105,6 +119,15 @@ public class AccountBuilder {
model.setPermissions(new ArrayList<>(permissions));
}
FieldSet selectedTenantFields = fields.extractPrefixed(BaseFieldSet.asIndexerPrefix(Account._selectedTenant));
if (!selectedTenantFields.isEmpty() && this.tenantScope.isSet()) {
if (!this.tenantScope.getTenantCode().equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())) {
TenantQuery query = this.queryFactory.query(TenantQuery.class).disableTracking().ids(this.tenantScope.getTenant());
model.setSelectedTenant(this.builderFactory.builder(TenantBuilder.class).build(selectedTenantFields, query.first()));
}
}
FieldSet profileFields = fields.extractPrefixed(BaseFieldSet.asIndexerPrefix(Account._profile));
if (!profileFields.isEmpty() && this.userScope.getUserIdSafe() != null){
model.setProfile(new Account.UserProfileInfo());

View File

@ -1,13 +1,13 @@
import { AppRole } from "@app/core/common/enum/app-role";
import { AppPermission } from "@app/core/common/enum/permission.enum";
import { Guid } from "@common/types/guid";
import { Tenant } from "../tenant/tenant";
export interface AppAccount {
isAuthenticated: boolean;
roles: AppRole[];
permissions: AppPermission[];
principal: AppPrincipalInfo;
profile: UserProfileInfo;
selectedTenant: Tenant;
}
export interface AppPrincipalInfo {

View File

@ -2,11 +2,12 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable, NgZone } from '@angular/core';
import { Router } from '@angular/router';
import { AppRole } from '@app/core/common/enum/app-role';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { AppAccount } from '@app/core/model/auth/principal';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseService } from '@common/base/base.service';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { KeycloakEventType, KeycloakService } from 'keycloak-angular';
@ -14,12 +15,9 @@ import { Observable, Subject, forkJoin, from, of } from 'rxjs';
import { exhaustMap, map, takeUntil } from 'rxjs/operators';
import { ConfigurationService } from '../configuration/configuration.service';
import { PrincipalService } from '../http/principal.service';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { TenantHandlingService } from '../tenant/tenant-handling.service';
export interface ResolutionContext {
roles: AppRole[];
permissions: AppPermission[];
}
export interface AuthenticationState {
@ -194,6 +192,7 @@ export class AuthService extends BaseService {
map(
(myTenants) => {
if (myTenants) {
if (myTenants.some(x => x.code.toLocaleLowerCase() == tenantCode.toLocaleLowerCase())) {
this.selectedTenant(tenantCode);
} else {
@ -228,12 +227,13 @@ export class AuthService extends BaseService {
return null;
}
public getRoles(): AppRole[] {
if (this.appAccount && this.appAccount.roles) {
return this.appAccount.roles;
public getSelectedTenantName(): string {
if (this.appAccount && this.appAccount.selectedTenant) {
return this.appAccount.selectedTenant.name;
}
return null;
}
public getUserProfileEmail(): string {
if (this.appAccount && this.appAccount.profile) {
return this.appAccount.profile.email;
@ -248,28 +248,6 @@ export class AuthService extends BaseService {
return null;
}
public hasAnyRole(roles: AppRole[]): boolean {
if (!roles) {
return false;
}
return roles.filter((r) => this.hasRole(r)).length > 0;
}
public hasRole(role: AppRole): boolean {
if (role === undefined) {
return false;
}
if (
!this.appAccount ||
!this.appAccount.roles ||
this.appAccount.roles.length === 0
) {
return false;
}
return this.appAccount.roles
.includes(role);
}
public getUserProfileCulture(): string {
if (this.appAccount && this.appAccount.profile) {
return this.appAccount.profile.culture;
@ -388,7 +366,6 @@ export class AuthService extends BaseService {
}
private evaluatePermission(availablePermissions: string[], permissionToCheck: string): boolean {
if (!permissionToCheck) { return false; }
// if (this.hasRole(AppRole.Admin)) { return true; }
return availablePermissions.map(x => x.toLowerCase()).includes(permissionToCheck.toLowerCase());
}
public hasAnyPermission(permissions: AppPermission[]): boolean {
@ -398,19 +375,14 @@ export class AuthService extends BaseService {
public authorize(context: ResolutionContext): boolean {
if (!context || this.hasRole(AppRole.Admin)) { return true; }
let roleAuthorized = false;
if (context.roles && context.roles.length > 0) {
roleAuthorized = this.hasAnyRole(context.roles);
}
if (!context) { return true; }
let permissionAuthorized = false;
if (context.permissions && context.permissions.length > 0) {
permissionAuthorized = this.hasAnyPermission(context.permissions);
}
if (roleAuthorized || permissionAuthorized) { return true; }
if (permissionAuthorized) { return true; }
return false;
}

View File

@ -1,35 +1,34 @@
import { Injectable } from '@angular/core';
import { AnnotationProtectionType } from '@app/core/common/enum/annotation-protection-type.enum';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionTemplateExternalSelectAuthType } from '@app/core/common/enum/description-template-external-select-auth-type';
import { DescriptionTemplateExternalSelectHttpMethodType } from '@app/core/common/enum/description-template-external-select-http-method-type';
import { DescriptionTemplateFieldDataExternalDatasetType } from '@app/core/common/enum/description-template-field-data-external-dataset-type';
import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-template-field-type';
import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { DmpBlueprintFieldCategory } from '@app/core/common/enum/dmp-blueprint-field-category';
import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status';
import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type';
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
import { DmpUserType } from '@app/core/common/enum/dmp-user-type';
import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type';
import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { PrefillingSourceSystemTargetType } from '@app/core/common/enum/prefilling-source-system-target-type';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type';
import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type';
import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type';
import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type';
import { SupportiveMaterialFieldType } from '@app/core/common/enum/supportive-material-field-type';
import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-description-template-role';
import { TranslateService } from '@ngx-translate/core';
import { AppRole } from '../../common/enum/app-role';
import { DmpBlueprintExtraFieldDataType } from '../../common/enum/dmp-blueprint-field-type';
import { DmpBlueprintType } from '../../common/enum/dmp-blueprint-type';
import { DmpStatus } from '../../common/enum/dmp-status';
import { ValidationType } from '../../common/enum/validation-type';
import { DescriptionTemplateExternalSelectAuthType } from '@app/core/common/enum/description-template-external-select-auth-type';
import { DmpBlueprintFieldCategory } from '@app/core/common/enum/dmp-blueprint-field-category';
import { DmpUserType } from '@app/core/common/enum/dmp-user-type';
import { PrefillingSourceSystemTargetType } from '@app/core/common/enum/prefilling-source-system-target-type';
import { AnnotationProtectionType } from '@app/core/common/enum/annotation-protection-type.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
@Injectable()
export class EnumUtils {

View File

@ -313,9 +313,9 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple
}
ngOnDestroy() {
this.panelClosedSubscription.unsubscribe();
this.stateChanges.complete();
this.fm.stopMonitoring(this.elRef.nativeElement);
this.panelClosedSubscription?.unsubscribe();
this.stateChanges?.complete();
this.fm?.stopMonitoring(this.elRef?.nativeElement);
}
//Configuration getters

View File

@ -4,7 +4,6 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatMenuTrigger } from '@angular/material/menu';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { AppRole } from '@app/core/common/enum/app-role';
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
import { StorageFile } from '@app/core/model/storage-file/storage-file';
import { LogoTenantConfiguration, TenantConfiguration } from '@app/core/model/tenant-configuaration/tenant-configuration';
@ -17,6 +16,7 @@ import { ProgressIndicationService } from '@app/core/services/progress-indicatio
import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice';
import { StorageFileService } from '@app/core/services/storage-file/storage-file.service';
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
import { UserService } from '@app/core/services/user/user.service';
import { BaseComponent } from '@common/base/base.component';
import { InAppNotificationService } from '@notification-service/services/http/inapp-notification.service';
import { MineInAppNotificationListingDialogComponent } from '@notification-service/ui/inapp-notification/listing-dialog/mine-inapp-notification-listing-dialog.component';
@ -26,7 +26,6 @@ import { nameof } from 'ts-simple-nameof';
import { StartNewDmpDialogComponent } from '../dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component';
import { FaqDialogComponent } from '../faq/dialog/faq-dialog.component';
import { UserDialogComponent } from './user-dialog/user-dialog.component';
import { UserService } from '@app/core/services/user/user.service';
@Component({
selector: 'app-navbar',
@ -270,10 +269,6 @@ export class NavbarComponent extends BaseComponent implements OnInit {
(ev.target as HTMLImageElement).src = this.getDefaultAvatar();
}
public isAdmin(): boolean {
return this.authentication.hasRole(AppRole.Admin);
}
openProfile() {
const dialogRef = this.dialog.open(UserDialogComponent, {
hasBackdrop: true,

View File

@ -3,14 +3,12 @@ import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { TranslateService } from '@ngx-translate/core';
import { AppRole } from '../../core/common/enum/app-role';
import { AuthService, LoginStatus } from '../../core/services/auth/auth.service';
import { AuthService } from '../../core/services/auth/auth.service';
import { LanguageDialogComponent } from '../language/dialog/language-dialog.component';
import { UserDialogComponent } from '../navbar/user-dialog/user-dialog.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { takeUntil } from 'rxjs/operators';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
declare interface RouteInfo {
path: string;
@ -151,14 +149,6 @@ export class SidebarComponent implements OnInit {
return this.authentication.currentAccountIsAuthenticated();
}
public isAdmin(): boolean {
return this.authentication.hasRole(AppRole.Admin);
}
public hasPermission(role: AppRole): boolean {
return this.authentication.hasRole(role);
}
isLoginRouteActivated(): boolean {
return this.location.path().indexOf('/login') > -1;
}