From 9cdebe01d7ea650ecd3154e7f3d3549db077adac Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 31 Oct 2025 14:16:12 +0100 Subject: [PATCH 01/16] Refactor anonymous authentication handling and cleanup subscription logic Replaced 'jwtHeader' with 'anonymousTokenHeader' for better clarity in the anonymous authentication service. Introduced an `isAnonymous` method in the user model to check user anonymity. Commented out unused subscription logic in `NextGroupService` for improved maintainability. --- .../anonymous/anonymous.service.ts | 20 ++++++++-------- .../anonymous-authentication-interceptor.ts | 10 ++++---- .../services/authentication-interceptor.ts | 2 +- .../permission/access.service.ts | 2 +- .../lib/groups/services/next-group.service.ts | 23 ++----------------- .../src/lib/user/models/user.ts | 4 ++++ 6 files changed, 23 insertions(+), 38 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts b/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts index 2ed898ec8e..1e69e625ee 100644 --- a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts +++ b/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts @@ -9,20 +9,20 @@ import {BehaviorSubject, Observable} from 'rxjs'; }) export class AnonymousService implements OnDestroy { - public static readonly JWT_BEARER_HEADER_DEFAULT = 'X-Jwt-Token'; - protected readonly _jwtHeader: string; + public static readonly X_ANONYMOUS_TOKEN = 'X-Anonymous-Token'; + protected readonly _anonymousTokenHeader: string; protected _storage: Storage; protected _tokenSet: BehaviorSubject; constructor(protected _config: ConfigurationService) { - this._jwtHeader = this._config.get().providers.auth.jwtBearer ? - this._config.get().providers.auth.jwtBearer : AnonymousService.JWT_BEARER_HEADER_DEFAULT; + this._anonymousTokenHeader = this._config.get().providers.auth.anonymous ? + this._config.get().providers.auth.anonymous : AnonymousService.X_ANONYMOUS_TOKEN; this._storage = this.resolveStorage(this._config.get().providers.auth['local']); this._tokenSet = new BehaviorSubject(false); } - get jwtHeader(): string { - return this._jwtHeader; + get anonymousTokenHeader(): string { + return this._anonymousTokenHeader; } get tokenSet(): Observable { @@ -30,22 +30,22 @@ export class AnonymousService implements OnDestroy { } public getToken(): string { - return this._storage.getItem(this._jwtHeader); + return this._storage.getItem(this._anonymousTokenHeader); } public setToken(token: string): void { - this._storage.setItem(this._jwtHeader, token); + this._storage.setItem(this._anonymousTokenHeader, token); if (!this._tokenSet.getValue()) this._tokenSet.next(true); } public removeToken(): void { - this._storage.removeItem(this._jwtHeader); + this._storage.removeItem(this._anonymousTokenHeader); this._tokenSet.next(false); } ngOnDestroy(): void { - localStorage.removeItem(this._jwtHeader); + localStorage.removeItem(this._anonymousTokenHeader); this._tokenSet.complete(); } diff --git a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts index f02ce95dc8..91f79c14ee 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts @@ -10,22 +10,22 @@ export class AnonymousAuthenticationInterceptor implements HttpInterceptor { constructor(protected _anonymousService: AnonymousService) {} intercept(req: HttpRequest, next: HttpHandler): Observable> { - const jwtAuthToken = this._anonymousService.getToken(); + const xAnonymousToken = this._anonymousService.getToken(); if (!this._anonymousService) { next.handle(req); } - if (!!jwtAuthToken) { + if (!!xAnonymousToken) { req = req.clone({ - headers: req.headers.set(this._anonymousService.jwtHeader, jwtAuthToken) + headers: req.headers.set(this._anonymousService.anonymousTokenHeader, xAnonymousToken) }); } return next.handle(req).pipe( tap(event => { if (event instanceof HttpResponse) { - if (event.headers.has(this._anonymousService.jwtHeader)) { - this._anonymousService.setToken(event.headers.get(this._anonymousService.jwtHeader)); + if (event.headers.has(this._anonymousService.anonymousTokenHeader)) { + this._anonymousService.setToken(event.headers.get(this._anonymousService.anonymousTokenHeader)); } } }), diff --git a/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts b/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts index 7e56051d92..ba254dd3d8 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts @@ -37,7 +37,7 @@ export class AuthenticationInterceptor implements HttpInterceptor { return next.handle(req).pipe( tap(event => { if (event instanceof HttpResponse) { - if (event.headers.has(this._session.sessionHeader) && !event.headers.has(this._anonymousService.jwtHeader)) { + if (event.headers.has(this._session.sessionHeader) && !event.headers.has(this._anonymousService.anonymousTokenHeader)) { this._session.setVerifiedToken(event.headers.get(this._session.sessionHeader)); } } diff --git a/projects/netgrif-components-core/src/lib/authorization/permission/access.service.ts b/projects/netgrif-components-core/src/lib/authorization/permission/access.service.ts index d02b79728b..c96b47250c 100644 --- a/projects/netgrif-components-core/src/lib/authorization/permission/access.service.ts +++ b/projects/netgrif-components-core/src/lib/authorization/permission/access.service.ts @@ -37,7 +37,7 @@ export class AccessService { if (view.access !== 'private') { throw new Error(`Unknown access option '${view.access}'. Only 'public' or 'private' is allowed.`); } - return !this._userService.user.isEmpty(); + return !this._userService.user.isEmpty() && !this._userService.user.isAnonymous(); } if (!url) { diff --git a/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts b/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts index 759a934fda..7a2af97fec 100644 --- a/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts +++ b/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts @@ -25,35 +25,16 @@ export class NextGroupService implements OnDestroy { protected _memberGroups$: BehaviorSubject>; protected _ownerGroups$: BehaviorSubject>; - private _userSub: Subscription; + // private _userSub: Subscription; constructor(protected _userService: UserService, protected _caseResourceService: CaseResourceService) { this._ownerGroups$ = new BehaviorSubject>([]); this._memberGroups$ = new BehaviorSubject>([]); - this._userSub = this._userService.user$.pipe( - switchMap(user => { - if (!user || user.id === '') { - return of([]); - } - - const params = new HttpParams().set(PaginationParams.PAGE_SIZE, `${(user as any).nextGroups.length}`); - - return this._caseResourceService.searchCases(SimpleFilter.fromCaseQuery({id: (user as any).nextGroups}), params) - .pipe( - map(page => page.content ? page.content : []), - map(groups => groups.filter(group => group.author.fullName !== 'application engine')) - ); - }) - ).subscribe(groups => { - const ownerGroups = groups.filter(g => g.author.email === this._userService.user.email); - this._ownerGroups$.next(ownerGroups); - this._memberGroups$.next(groups); - }); } ngOnDestroy(): void { - this._userSub.unsubscribe(); + // this._userSub.unsubscribe(); this._memberGroups$.complete(); this._ownerGroups$.complete(); } diff --git a/projects/netgrif-components-core/src/lib/user/models/user.ts b/projects/netgrif-components-core/src/lib/user/models/user.ts index 86786fc2ca..2bfe3c4f90 100644 --- a/projects/netgrif-components-core/src/lib/user/models/user.ts +++ b/projects/netgrif-components-core/src/lib/user/models/user.ts @@ -60,4 +60,8 @@ export class User implements IUser { return !!this.impersonated; } + public isAnonymous(): boolean { + return this.authorities.some(a => a === 'ANONYMOUS_USER'); + } + } From 8babfe3921bdf523c4a355701efa8e5cc67770d9 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 21 Jan 2026 10:04:02 +0100 Subject: [PATCH 02/16] Remove public resource services and simplify related factories Removed `PublicCaseResourceService`, `PublicPetriNetResourceService`, `PublicTaskResourceService`, and `PublicProcessService`, consolidating their functionality into existing services. Updated providers and API usage to eliminate redundant public service factories and streamline resource access logic. --- .../src/lib/process/public-api.ts | 1 - .../process/public-process.service.spec.ts | 27 --- .../src/lib/process/public-process.service.ts | 13 -- .../case-resource-service.provider.ts | 35 +--- .../petrinet-resource-service.provider.ts | 41 +--- .../process-service.provider.ts | 41 +--- .../task-resource-service.provider.ts | 36 +--- .../abstract-resource.service.ts | 4 + .../engine-endpoint/case-resource.service.ts | 5 +- .../petri-net-resource.service.ts | 11 +- .../public-case-resource.service.spec.ts | 27 --- .../public/public-case-resource.service.ts | 38 ---- .../public-petri-net-resource.service.spec.ts | 27 --- .../public-petri-net-resource.service.ts | 109 ---------- .../public-task-resource.service.spec.ts | 27 --- .../public/public-task-resource.service.ts | 193 ------------------ .../engine-endpoint/task-resource.service.ts | 4 +- .../src/lib/resources/public-api.ts | 5 - .../src/lib/user/models/user.ts | 2 +- 19 files changed, 34 insertions(+), 612 deletions(-) delete mode 100644 projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts delete mode 100644 projects/netgrif-components-core/src/lib/process/public-process.service.ts delete mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts delete mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts delete mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts delete mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts delete mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts delete mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts diff --git a/projects/netgrif-components-core/src/lib/process/public-api.ts b/projects/netgrif-components-core/src/lib/process/public-api.ts index 37e8f99ef5..a79bf4e654 100644 --- a/projects/netgrif-components-core/src/lib/process/public-api.ts +++ b/projects/netgrif-components-core/src/lib/process/public-api.ts @@ -3,7 +3,6 @@ export * from './net'; export * from './transition'; export * from './transaction'; export * from './netRole'; -export * from './public-process.service'; export * from './petri-net-reference-with-permissions'; export * from './permissions'; export * from './rolesAndPermissions' diff --git a/projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts b/projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts deleted file mode 100644 index cc14c60b4b..0000000000 --- a/projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {TestBed} from '@angular/core/testing'; - -import {PublicProcessService} from './public-process.service'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {ConfigurationService} from '../configuration/configuration.service'; -import {TestConfigurationService} from '../utility/tests/test-config'; - -describe('PublicProcessService', () => { - let service: PublicProcessService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, NoopAnimationsModule], - providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] - }); - service = TestBed.inject(PublicProcessService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - afterEach(() => { - TestBed.resetTestingModule(); - }); -}); diff --git a/projects/netgrif-components-core/src/lib/process/public-process.service.ts b/projects/netgrif-components-core/src/lib/process/public-process.service.ts deleted file mode 100644 index f559836f4c..0000000000 --- a/projects/netgrif-components-core/src/lib/process/public-process.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable } from '@angular/core'; -import {ProcessService} from './process.service'; -import {PublicPetriNetResourceService} from '../resources/engine-endpoint/public/public-petri-net-resource.service'; -import {LoggerService} from '../logger/services/logger.service'; - -@Injectable({ - providedIn: 'root' -}) -export class PublicProcessService extends ProcessService { - constructor(private _publicPetriNetResource: PublicPetriNetResourceService, private _logger: LoggerService) { - super(_publicPetriNetResource, _logger); - } -} diff --git a/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts index 0db74d6de6..f386515f56 100644 --- a/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts @@ -1,45 +1,16 @@ -import { Router } from '@angular/router'; import { CaseResourceService } from '../../resources/engine-endpoint/case-resource.service'; -import { UserService } from '../../user/services/user.service'; -import { SessionService } from '../../authentication/session/services/session.service'; -import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; -import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; import { ResourceProvider } from '../../resources/resource-provider.service'; import { ConfigurationService } from '../../configuration/configuration.service'; -import { RedirectService } from '../../routing/redirect-service/redirect.service'; -import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; -import { PublicCaseResourceService } from '../../resources/engine-endpoint/public/public-case-resource.service'; export const CaseResourceServiceProvider = { provide: CaseResourceService, - useFactory: (userService: UserService, - sessionService: SessionService, - authService: AuthenticationService, - router: Router, - publicResolverService: PublicUrlResolverService, - provider: ResourceProvider, - config: ConfigurationService, - redirectService: RedirectService) => { - return publicFactoryResolver( - userService, - sessionService, - authService, - router, - publicResolverService, - new CaseResourceService(provider, config), - new PublicCaseResourceService(provider, config), - redirectService - ); + useFactory: (provider: ResourceProvider, + config: ConfigurationService) => { + return new CaseResourceService(provider, config); }, deps: [ - UserService, - SessionService, - AuthenticationService, - Router, - PublicUrlResolverService, ResourceProvider, ConfigurationService, - RedirectService ] } diff --git a/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts index fae0fbf666..645e4fc9a3 100644 --- a/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts @@ -1,47 +1,14 @@ - -import { Router } from '@angular/router'; -import { UserService } from '../../user/services/user.service'; import { PetriNetResourceService } from '../../resources/engine-endpoint/petri-net-resource.service'; -import { SessionService } from '../../authentication/session/services/session.service'; -import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; -import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; import { ResourceProvider } from '../../resources/resource-provider.service'; import { ConfigurationService } from '../../configuration/configuration.service'; -import { RedirectService } from '../../routing/redirect-service/redirect.service'; -import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; -import { - PublicPetriNetResourceService -} from '../../resources/engine-endpoint/public/public-petri-net-resource.service'; - export const PetriNetResourceServiceProvider = { provide: PetriNetResourceService, - useFactory: (userService: UserService, - sessionService: SessionService, - authService: AuthenticationService, - router: Router, - publicResolverService: PublicUrlResolverService, - provider: ResourceProvider, - config: ConfigurationService, - redirectService: RedirectService) => { - return publicFactoryResolver( - userService, - sessionService, - authService, - router, - publicResolverService, - new PetriNetResourceService(provider, config), - new PublicPetriNetResourceService(provider, config), - redirectService - ); + useFactory: (provider: ResourceProvider, + config: ConfigurationService) => { + return new PetriNetResourceService(provider, config); }, deps: [ - UserService, - SessionService, - AuthenticationService, - Router, - PublicUrlResolverService, ResourceProvider, - ConfigurationService, - RedirectService] + ConfigurationService] } diff --git a/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts index c21d8f4975..22142222ae 100644 --- a/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts @@ -1,48 +1,15 @@ - -import { Router } from '@angular/router'; import { ProcessService } from '../../process/process.service'; -import { UserService } from '../../user/services/user.service'; -import { SessionService } from '../../authentication/session/services/session.service'; -import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; -import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; import { PetriNetResourceService } from '../../resources/engine-endpoint/petri-net-resource.service'; -import { PublicPetriNetResourceService } from '../../resources/engine-endpoint/public/public-petri-net-resource.service'; import { LoggerService } from '../../logger/services/logger.service'; -import { RedirectService } from '../../routing/redirect-service/redirect.service'; -import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; -import { PublicProcessService } from '../../process/public-process.service'; export const ProcessServiceProvider = { provide: ProcessService, - useFactory: (userService: UserService, - sessionService: SessionService, - authService: AuthenticationService, - router: Router, - publicResolverService: PublicUrlResolverService, - petriNetResource: PetriNetResourceService, - publicPetriNetResource: PublicPetriNetResourceService, - loggerService: LoggerService, - redirectService: RedirectService) => { - return publicFactoryResolver( - userService, - sessionService, - authService, - router, - publicResolverService, - new ProcessService(petriNetResource, loggerService), - new PublicProcessService(publicPetriNetResource, loggerService), - redirectService - ); + useFactory: (petriNetResource: PetriNetResourceService, + loggerService: LoggerService) => { + return new ProcessService(petriNetResource, loggerService); }, deps: [ - UserService, - SessionService, - AuthenticationService, - Router, - PublicUrlResolverService, PetriNetResourceService, - PublicPetriNetResourceService, - LoggerService, - RedirectService + LoggerService ] } diff --git a/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts index dd75860542..9a7ba24514 100644 --- a/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts @@ -1,50 +1,22 @@ -import { Router } from '@angular/router'; -import { UserService } from '../../user/services/user.service'; import { TaskResourceService } from '../../resources/engine-endpoint/task-resource.service'; -import { SessionService } from '../../authentication/session/services/session.service'; -import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; -import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; import { LoggerService } from '../../logger/services/logger.service'; import { ResourceProvider } from '../../resources/resource-provider.service'; import { ConfigurationService } from '../../configuration/configuration.service'; import { FieldConverterService } from '../../task-content/services/field-converter.service'; -import { RedirectService } from '../../routing/redirect-service/redirect.service'; -import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; -import { PublicTaskResourceService } from '../../resources/engine-endpoint/public/public-task-resource.service'; export const TaskResourceServiceProvider = { provide: TaskResourceService, useFactory: ( - userService: UserService, - sessionService: SessionService, - authService: AuthenticationService, - router: Router, - publicResolverService: PublicUrlResolverService, logger: LoggerService, provider: ResourceProvider, config: ConfigurationService, - fieldConverter: FieldConverterService, - redirectService: RedirectService) => { - return publicFactoryResolver( - userService, - sessionService, - authService, - router, - publicResolverService, - new TaskResourceService(provider, config, fieldConverter, logger), - new PublicTaskResourceService(provider, config, fieldConverter, logger), - redirectService - ); + fieldConverter: FieldConverterService) => { + return new TaskResourceService(provider, config, fieldConverter, logger); }, deps: [ - UserService, - SessionService, - AuthenticationService, - Router, - PublicUrlResolverService, LoggerService, ResourceProvider, ConfigurationService, - FieldConverterService, - RedirectService] + FieldConverterService + ] } diff --git a/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts index 28627c1308..66e0be11ad 100644 --- a/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts @@ -122,4 +122,8 @@ export abstract class AbstractResourceService { } }; } + + protected resolvePublicUrl(baseUrl: string, isUserAnonymous: boolean): string { + return isUserAnonymous ? baseUrl : baseUrl + '/public'; + } } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts index 67b4c9763b..240091e3fb 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts @@ -15,13 +15,16 @@ import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.se import {EventOutcomeMessageResource} from '../interface/message-resource'; import {CreateCaseRequestBody} from '../interface/create-case-request-body'; import {HttpParams} from "@angular/common/http"; +import {UserService} from "../../user/services/user.service"; @Injectable({ providedIn: 'root' }) export class CaseResourceService extends AbstractResourceService implements CountService { - constructor(provider: ResourceProvider, configService: ConfigurationService) { + constructor(provider: ResourceProvider, + configService: ConfigurationService, + protected userService: UserService) { super('case', provider, configService); } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts index 868ba25350..292162c6ac 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts @@ -14,13 +14,16 @@ import {Page} from '../interface/page'; import {processMessageResponse} from '../../utility/process-message-response'; import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.service'; import RolesAndPermissions from '../../process/rolesAndPermissions'; +import {UserService} from "../../user/services/user.service"; @Injectable({ providedIn: 'root' }) export class PetriNetResourceService extends AbstractResourceService { - constructor(provider: ResourceProvider, configService: ConfigurationService) { + constructor(provider: ResourceProvider, + configService: ConfigurationService, + protected userService: UserService) { super('petrinet', provider, configService); } @@ -45,7 +48,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/data */ public getDataPetriNet(body: object): Observable { // TODO: response - return this._resourceProvider.post$('petrinet/data', this.SERVER_URL, body) + return this._resourceProvider.post$(this.resolvePublicUrl('petrinet', this.userService.user.isAnonymous()) + '/data', this.SERVER_URL, body) .pipe(map(r => this.changeType(r, undefined))); } @@ -57,7 +60,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/transitions */ public getPetriNetTransitions(netId: string): Observable> { - return this._resourceProvider.get$('/petrinet/transitions', this.SERVER_URL, new HttpParams().set('ids', netId)) + return this._resourceProvider.get$(this.resolvePublicUrl('petrinet', this.userService.user.isAnonymous()) + '/transitions', this.SERVER_URL, new HttpParams().set('ids', netId)) .pipe(map(r => this.changeType(r, 'transitionReferences'))); } @@ -69,7 +72,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id}/transactions */ public getPetriNetTransactions(netId: string, params?: Params): Observable> { - return this._resourceProvider.get$('/petrinet/' + netId + '/transactions', this.SERVER_URL, params) + return this._resourceProvider.get$(this.resolvePublicUrl('petrinet', this.userService.user.isAnonymous()) + netId + '/transactions', this.SERVER_URL, params) .pipe(map(r => this.changeType(r, 'transactions'))); } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts deleted file mode 100644 index 89281b968a..0000000000 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {TestBed} from '@angular/core/testing'; - -import {PublicCaseResourceService} from './public-case-resource.service'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {ConfigurationService} from '../../../configuration/configuration.service'; -import {TestConfigurationService} from '../../../utility/tests/test-config'; - -describe('PublicCaseResourceService', () => { - let service: PublicCaseResourceService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, NoopAnimationsModule], - providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] - }); - service = TestBed.inject(PublicCaseResourceService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - afterEach( () => { - TestBed.resetTestingModule(); - }); -}); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts deleted file mode 100644 index 508dc2b606..0000000000 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {Injectable} from '@angular/core'; - -import {Observable} from 'rxjs'; -import {map} from 'rxjs/operators'; -import {CaseResourceService} from '../case-resource.service'; -import {ResourceProvider} from '../../resource-provider.service'; -import {ConfigurationService} from '../../../configuration/configuration.service'; -import {EventOutcomeMessageResource} from '../../interface/message-resource'; - -@Injectable({ - providedIn: 'root' -}) -export class PublicCaseResourceService extends CaseResourceService { - - constructor(provider: ResourceProvider, configService: ConfigurationService) { - super(provider, configService); - } - - /** - * Get all case data - * GET - * {{baseUrl}}/api/public/case/:id/data - */ - public getCaseData(caseID: string): Observable { - return this._resourceProvider.get$('public/case/' + caseID + '/data', this.SERVER_URL) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Create new case - * POST - * {{baseUrl}}/api/workflow/case - */ - public createCase(body: object): Observable { - return this._resourceProvider.post$('public/case/', this.SERVER_URL, body) - .pipe(map(r => this.changeType(r, undefined))); - } -} diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts deleted file mode 100644 index be67439287..0000000000 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {TestBed} from '@angular/core/testing'; - -import {PublicPetriNetResourceService} from './public-petri-net-resource.service'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {ConfigurationService} from '../../../configuration/configuration.service'; -import {TestConfigurationService} from '../../../utility/tests/test-config'; - -describe('PublicPetriNetResourceService', () => { - let service: PublicPetriNetResourceService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, NoopAnimationsModule], - providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] - }); - service = TestBed.inject(PublicPetriNetResourceService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - afterEach(() => { - TestBed.resetTestingModule(); - }); -}); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts deleted file mode 100644 index 9b5f3b2e14..0000000000 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Injectable } from '@angular/core'; -import {PetriNetResourceService} from '../petri-net-resource.service'; -import {Params, ResourceProvider} from '../../resource-provider.service'; -import {ConfigurationService} from '../../../configuration/configuration.service'; -import {Observable} from 'rxjs'; -import {PetriNet} from '../../interface/petri-net'; -import {PetriNetReference} from '../../interface/petri-net-reference'; -import {map} from 'rxjs/operators'; -import {PetriNetRequestBody} from '../../interface/petri-net-request-body'; -import {Page} from '../../interface/page'; -import Transaction from '../../../process/transaction'; -import Transition from '../../../process/transition'; -import {HttpParams} from '@angular/common/http'; -import RolesAndPermissions from '../../../process/rolesAndPermissions'; - -@Injectable({ - providedIn: 'root' -}) -export class PublicPetriNetResourceService extends PetriNetResourceService { - - constructor(protected provider: ResourceProvider, protected _configService: ConfigurationService) { - super(provider, _configService); - } - - /** - * get One Net by ID - * - * **Request Type:** GET - * - * **Request URL:** {{baseUrl}}/api/public/petrinet/{id} - */ - public getOneById(netId: string, params?: Params): Observable { - return this.provider.get$('public/petrinet/' + netId, this.SERVER_URL, params) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * get One Net - * - * **Request Type:** GET - * - * **Request URL:** {{baseUrl}}/api/public/petrinet/{identifier}/{version} - */ - public getOne(identifier: string, version: string, params?: Params): Observable { - return this.provider.get$('public/petrinet/' + btoa(identifier) + '/' + version, this.SERVER_URL, params) - .pipe(map(r => this.changeType(r, 'petriNetReferences'))); - } - - /** - * search PetriNets - * - * **Request Type:** POST - * - * **Request URL:** {{baseUrl}}/api/petrinet/search - */ - public searchPetriNets(body: PetriNetRequestBody, params?: Params): Observable> { - return this._resourceProvider.post$('public/petrinet/search', this.SERVER_URL, body, params) - // .pipe(map(r => this.getResourcePage(r, 'petriNetReferences'))); - .pipe(map(r => this.mapToPage(r))); - } - - /** - * Get Roles References Using - * - * **Request Type:** GET - * - * **Request URL:** {{baseUrl}}/api/petrinet/{id}/roles - */ - public getPetriNetRoles(netId: string, params?: Params): Observable { - return this._resourceProvider.get$('public/petrinet/' + netId + '/roles', this.SERVER_URL, params) - .pipe(map(r => this.changeType(r, 'processRoles'))); - } - - /** - * Get Transaction References Using - * - * **Request Type:** GET - * - * **Request URL:** {{baseUrl}}/api/petrinet/{id}/transactions - */ - public getPetriNetTransactions(netId: string, params?: Params): Observable> { - return this._resourceProvider.get$('public/petrinet/' + netId + '/transactions', this.SERVER_URL, params) - .pipe(map(r => this.changeType(r, 'transactions'))); - } - - /** - * Get Data Field References Using - * - * **Request Type:** POST - * - * **Request URL:** {{baseUrl}}/api/petrinet/data - */ - public getDataPetriNet(body: object): Observable { // TODO: response - return this._resourceProvider.post$('public/petrinet/data', this.SERVER_URL, body) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Get Transition References Using - * - * **Request Type:** GET - * - * **Request URL:** {{baseUrl}}/api/petrinet/transitions - */ - public getPetriNetTransitions(netId: string): Observable> { - return this._resourceProvider.get$('public/petrinet/transitions', this.SERVER_URL, new HttpParams().set('ids', netId)) - .pipe(map(r => this.changeType(r, 'transitionReferences'))); - } -} diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts deleted file mode 100644 index c044130840..0000000000 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {TestBed} from '@angular/core/testing'; - -import {PublicTaskResourceService} from './public-task-resource.service'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {ConfigurationService} from '../../../configuration/configuration.service'; -import {TestConfigurationService} from '../../../utility/tests/test-config'; - -describe('PublicTaskResourceService', () => { - let service: PublicTaskResourceService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, NoopAnimationsModule], - providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] - }); - service = TestBed.inject(PublicTaskResourceService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - afterEach(() => { - TestBed.resetTestingModule(); - }); -}); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts deleted file mode 100644 index 9809009a72..0000000000 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts +++ /dev/null @@ -1,193 +0,0 @@ -import {Injectable} from '@angular/core'; -import {TaskResourceService} from '../task-resource.service'; -import {FieldConverterService} from '../../../task-content/services/field-converter.service'; -import {ConfigurationService} from '../../../configuration/configuration.service'; -import {Params, ProviderProgress, ResourceProvider} from '../../resource-provider.service'; -import {LoggerService} from '../../../logger/services/logger.service'; -import {Observable} from 'rxjs'; -import {filter, map} from 'rxjs/operators'; -import {FilterType} from '../../../filter/models/filter-type'; -import {Filter} from '../../../filter/models/filter'; -import {Page} from '../../interface/page'; -import {TaskSetDataRequestBody} from '../../interface/task-set-data-request-body'; -import {TaskReference} from '../../interface/task-reference'; -import {Task} from '../../interface/task'; -import {HttpEventType, HttpParams} from '@angular/common/http'; -import {EventOutcomeMessageResource, MessageResource} from '../../interface/message-resource'; -import {FileFieldRequest} from "../../interface/file-field-request-body"; - -@Injectable({ - providedIn: 'root' -}) -export class PublicTaskResourceService extends TaskResourceService { - - constructor(protected _provider: ResourceProvider, - protected _configService: ConfigurationService, - protected _fieldConverter: FieldConverterService, - protected _logger: LoggerService) { - super(_provider, _configService, _fieldConverter, _logger); - } - - /** - * Assign task - * GET - */ - // {{baseUrl}}/api/public/task/assign/:id - public assignTask(taskId: string): Observable { - return this._provider.get$('public/task/assign/' + taskId, this.SERVER_URL) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Cancel task - * GET - */ - // {{baseUrl}}/api/public/task/cancel/:id - public cancelTask(taskId: string): Observable { - return this._provider.get$('public/task/cancel/' + taskId, this.SERVER_URL) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Finish task - * GET - */ - // {{baseUrl}}/api/public/task/finish/:id - public finishTask(taskId: string): Observable { - return this._provider.get$('public/task/finish/' + taskId, this.SERVER_URL) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Get tasks of the case - * GET - */ - // {{baseUrl}}/api/public/task/case/:id - public getAllTasksByCase(caseId: string): Observable> { - return this._provider.get$('public/task/case/' + caseId, this.SERVER_URL) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Get all task data - * - * GET - * - * If you don't want to parse the response yourself use [getData]{@link TaskResourceService#getData} instead. - * - * @returns the raw backend response without any additional processing - */ - // {{baseUrl}}/api/public/task/:id/data - public rawGetData(taskId: string): Observable { - return this._provider.get$('public/task/' + taskId + '/data', this.SERVER_URL) - .pipe(map(r => this.changeType(r, 'dataGroups'))); - } - - /** - * Set task data - * POST - */ - // {{baseUrl}}/api/public/task/:id/data - public setData(taskId: string, body: TaskSetDataRequestBody): Observable { - return this._provider.post$('public/task/' + taskId + '/data', this.SERVER_URL, body) - .pipe(map(r => this.changeType(r, undefined))); - } - - /** - * Searches tasks trough the Mongo endpoint. - * POST - * @param filterParam filter used to search the tasks. Must be of type `TASK`. - * Note that the `query` attribute of the filter cannot be used with this endpoint. - * Attempting to use it will display a warning and remove the attribute from the request. - * @param params Additional request parameters - */ - // {{baseUrl}}/api/public/task/search - public getTasks(filterParam: Filter, params?: Params): Observable> { - if (filterParam.type !== FilterType.TASK) { - throw new Error('Provided filter doesn\'t have type TASK'); - } - - if (filterParam.bodyContainsQuery()) { - throw new Error('getTasks endpoint cannot be queried with filters that contain the \'query\' attribute'); - } - - params = ResourceProvider.combineParams(filterParam.getRequestParams(), params); - return this._provider.post$('public/task/search', this.SERVER_URL, filterParam.getRequestBody(), params) - .pipe(map(r => this.getResourcePage(r, 'tasks'))); - } - - /** - * Download task file field value - * GET - */ - public downloadFile(taskId: string, params: HttpParams): Observable { - const url = `public/task/${taskId}/file${params?.has("fileName") ? '/named' : ''}`; - return this._resourceProvider.getBlob$(url, this.SERVER_URL, params).pipe( - map(event => { - switch (event.type) { - case HttpEventType.DownloadProgress: - return ResourceProvider.getProgress(event); - case HttpEventType.Response: - return event.body; - default: - return undefined; - } - }), - filter(value => !!value) - ); - } - - /** - * Upload file into the task - * POST - */ - public uploadFile(taskId: string, body: object, multipleFiles: boolean): - Observable { - const url = `public/task/${taskId}/${multipleFiles ? 'files' : 'file'}`; - return this._resourceProvider.postWithEvent$(url, this.SERVER_URL, body).pipe( - map(event => { - switch (event.type) { - case HttpEventType.UploadProgress: - return ResourceProvider.getProgress(event); - case HttpEventType.Response: - return event.body; - default: - return undefined; - } - }), - filter(value => !!value) - ); - } - - /** - * Delete file from the task - * DELETE - */ - public deleteFile(taskId: string, body: FileFieldRequest): Observable { - const url = `public/task/${taskId}/file${body.fileName ? '/named' : ''}`; - return this._resourceProvider.delete$(url, this.SERVER_URL, {}, {}, 'json', body).pipe( - map(r => this.changeType(r, undefined)) - ); - } - - /** - * Download task file preview for field value - * GET - */ - public downloadFilePreview(taskId: string, params: HttpParams): Observable { - const url = `public/task/${taskId}/file_preview`; - return this._resourceProvider.getBlob$(url, this.SERVER_URL, params).pipe( - map(event => { - switch (event.type) { - case HttpEventType.DownloadProgress: - return ResourceProvider.getProgress(event); - case HttpEventType.Response: - return event.body; - default: - return undefined; - } - }), - filter(value => !!value) - ); - } -} diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts index ce3918d8d3..d7e639eae6 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts @@ -20,6 +20,7 @@ import {DataGroup} from '../interface/data-groups'; import {DataField} from '../../data-fields/models/abstract-data-field'; import {GetDataGroupsEventOutcome} from '../../event/model/event-outcomes/data-outcomes/get-data-groups-event-outcome'; import {FileFieldRequest} from "../interface/file-field-request-body"; +import {UserService} from "../../user/services/user.service"; @Injectable({ providedIn: 'root' @@ -29,7 +30,8 @@ export class TaskResourceService extends AbstractResourceService implements Coun constructor(provider: ResourceProvider, configService: ConfigurationService, protected _fieldConverter: FieldConverterService, - protected _logger: LoggerService) { + protected _logger: LoggerService, + protected userService: UserService) { super('task', provider, configService); } diff --git a/projects/netgrif-components-core/src/lib/resources/public-api.ts b/projects/netgrif-components-core/src/lib/resources/public-api.ts index 6ee3de7108..8a39c44212 100644 --- a/projects/netgrif-components-core/src/lib/resources/public-api.ts +++ b/projects/netgrif-components-core/src/lib/resources/public-api.ts @@ -8,11 +8,6 @@ export * from './engine-endpoint/dashboard-resource.service'; export * from './engine-endpoint/ldap-group-resource.service'; export * from './engine-endpoint/configuration-resource.service'; -/* PUBLIC SERVICES */ -export * from './engine-endpoint/public/public-case-resource.service'; -export * from './engine-endpoint/public/public-petri-net-resource.service'; -export * from './engine-endpoint/public/public-task-resource.service'; - export * from './interface/author'; export * from './interface/immediate-data'; export * from './interface/response-data'; diff --git a/projects/netgrif-components-core/src/lib/user/models/user.ts b/projects/netgrif-components-core/src/lib/user/models/user.ts index 2bfe3c4f90..6ce74f7918 100644 --- a/projects/netgrif-components-core/src/lib/user/models/user.ts +++ b/projects/netgrif-components-core/src/lib/user/models/user.ts @@ -61,7 +61,7 @@ export class User implements IUser { } public isAnonymous(): boolean { - return this.authorities.some(a => a === 'ANONYMOUS_USER'); + return this.authorities.length === 1 && this.authorities[0] === 'ANONYMOUS_USER'; } } From f0a63a8da3e2dc5190c437e57a3dd9319f6b3198 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 26 Jan 2026 15:51:54 +0100 Subject: [PATCH 03/16] Remove anonymous authentication support. The changes include commenting out code related to the AnonymousService and AnonymousAuthenticationInterceptor, removing anonymous-specific logic from services, tests, and configuration. This simplifies the codebase and prepares it to shift focus towards authenticated user functionality. --- .../nae-example-app/src/app/app.component.ts | 2 +- .../anonymous/anonymous.service.spec.ts | 56 ++++----- .../authentication/authentication.module.ts | 4 +- .../src/lib/authentication/public-api.ts | 2 +- ...onymous-authentication-interceptor.spec.ts | 116 +++++++++--------- .../anonymous-authentication-interceptor.ts | 82 ++++++------- .../services/authentication-interceptor.ts | 6 +- .../src/lib/filter/models/filter.ts | 10 ++ .../src/lib/filter/models/merged-filter.ts | 7 ++ .../src/lib/filter/models/simple-filter.ts | 14 +++ ...abstract-navigation-tree.component.spec.ts | 4 +- .../case-resource-service.provider.ts | 12 +- .../petrinet-resource-service.provider.ts | 10 +- .../process-service.provider.ts | 11 +- .../task-resource-service.provider.ts | 18 +-- .../abstract-resource.service.ts | 12 +- .../engine-endpoint/case-resource.service.ts | 2 +- .../petri-net-resource.service.ts | 16 +-- .../task-resource.service.spec.ts | 4 +- .../engine-endpoint/task-resource.service.ts | 26 ++-- .../task/services/assign-policy.service.ts | 7 +- .../src/lib/user/models/user.ts | 2 +- .../user/services/user-preference.service.ts | 48 ++++---- .../src/lib/user/services/user.service.ts | 66 +++++----- .../task-view/service/task-view.service.ts | 5 +- 25 files changed, 269 insertions(+), 273 deletions(-) diff --git a/projects/nae-example-app/src/app/app.component.ts b/projects/nae-example-app/src/app/app.component.ts index f63663c479..b8dd1cd273 100644 --- a/projects/nae-example-app/src/app/app.component.ts +++ b/projects/nae-example-app/src/app/app.component.ts @@ -28,7 +28,7 @@ export class AppComponent { translate.setTranslation('en', en, true); translate.setTranslation('sk', sk, true); - this.userService.user$.pipe(filter(u => !!u && u.id !== ''), take(1)).subscribe(() => { + this.userService.user$.pipe(filter(u => !!u && u.id !== '' && !u.isAnonymous()), take(1)).subscribe(() => { const allNets = allowedNetsFactory.createWithAllNets(); allNets.allowedNetsIdentifiers$.pipe(take(1)).subscribe(nets => { if (this.baseAllowedNets.allowedNets.length !== 0) { diff --git a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts b/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts index 93ef00b802..c0031c9334 100644 --- a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts @@ -1,28 +1,28 @@ -import {TestBed} from '@angular/core/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {RouterTestingModule} from '@angular/router/testing'; -import {AnonymousService} from './anonymous.service'; -import {ConfigurationService} from '../../configuration/configuration.service'; -import {TestConfigurationService} from '../../utility/tests/test-config'; - -describe('AnonymousService', () => { - let service: AnonymousService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, HttpClientTestingModule, RouterTestingModule.withRoutes([])], - providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] - }); - service = TestBed.inject(AnonymousService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - afterEach(() => { - TestBed.resetTestingModule(); - }); -}); - +// import {TestBed} from '@angular/core/testing'; +// import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +// import {HttpClientTestingModule} from '@angular/common/http/testing'; +// import {RouterTestingModule} from '@angular/router/testing'; +// import {AnonymousService} from './anonymous.service'; +// import {ConfigurationService} from '../../configuration/configuration.service'; +// import {TestConfigurationService} from '../../utility/tests/test-config'; +// +// describe('AnonymousService', () => { +// let service: AnonymousService; +// +// beforeEach(() => { +// TestBed.configureTestingModule({ +// imports: [NoopAnimationsModule, HttpClientTestingModule, RouterTestingModule.withRoutes([])], +// providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] +// }); +// service = TestBed.inject(AnonymousService); +// }); +// +// it('should be created', () => { +// expect(service).toBeTruthy(); +// }); +// +// afterEach(() => { +// TestBed.resetTestingModule(); +// }); +// }); +// diff --git a/projects/netgrif-components-core/src/lib/authentication/authentication.module.ts b/projects/netgrif-components-core/src/lib/authentication/authentication.module.ts index b6e1f2c204..771ad7eef8 100644 --- a/projects/netgrif-components-core/src/lib/authentication/authentication.module.ts +++ b/projects/netgrif-components-core/src/lib/authentication/authentication.module.ts @@ -6,7 +6,7 @@ import {ProxyAuthenticationService} from './proxyAuthentication.service'; import {AuthenticationMethodService} from './services/authentication-method.service'; import {OverlayModule} from '@angular/cdk/overlay'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; -import {AnonymousAuthenticationInterceptor} from './services/anonymous-authentication-interceptor'; +// import {AnonymousAuthenticationInterceptor} from './services/anonymous-authentication-interceptor'; @NgModule({ @@ -22,7 +22,7 @@ import {AnonymousAuthenticationInterceptor} from './services/anonymous-authentic ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthenticationInterceptor, multi: true }, - { provide: HTTP_INTERCEPTORS, useClass: AnonymousAuthenticationInterceptor, multi: true }, + // { provide: HTTP_INTERCEPTORS, useClass: AnonymousAuthenticationInterceptor, multi: true }, { provide: AuthenticationMethodService, useClass: ProxyAuthenticationService}, // AuthenticationEffects ] diff --git a/projects/netgrif-components-core/src/lib/authentication/public-api.ts b/projects/netgrif-components-core/src/lib/authentication/public-api.ts index ecd362b905..b7b1c6a414 100644 --- a/projects/netgrif-components-core/src/lib/authentication/public-api.ts +++ b/projects/netgrif-components-core/src/lib/authentication/public-api.ts @@ -10,7 +10,7 @@ export * from './authentication.module'; /* SERVICES */ export * from './anonymous/anonymous.service'; -export * from './services/anonymous-authentication-interceptor' +// export * from './services/anonymous-authentication-interceptor' export * from './services/authentication-interceptor' export * from './proxyAuthentication.service' diff --git a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts index 594f185f1d..220bfa99fb 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts @@ -1,58 +1,58 @@ -import {inject, TestBed} from '@angular/core/testing'; -import {ConfigurationService} from '../../configuration/configuration.service'; -import {TestConfigurationService} from '../../utility/tests/test-config'; -import {HTTP_INTERCEPTORS, HttpClient, HttpHeaders} from '@angular/common/http'; -import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; -import {RouterTestingModule} from '@angular/router/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {LoggerService} from '../../logger/services/logger.service'; -import {AnonymousService} from '../anonymous/anonymous.service'; -import {AnonymousAuthenticationInterceptor} from './anonymous-authentication-interceptor'; - -describe('AnonymousAuthenticationInterceptor', () => { - let service: AnonymousService; - let warnSpy: jasmine.Spy; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])], - providers: [ - {provide: ConfigurationService, useClass: TestConfigurationService}, - AnonymousService, - { - provide: HTTP_INTERCEPTORS, - useClass: AnonymousAuthenticationInterceptor, - multi: true - } - ] - }); - service = TestBed.inject(AnonymousService); - warnSpy = spyOn(TestBed.inject(LoggerService), 'warn'); - }); - - describe('intercept HTTP request', () => { - it('should add JWT bearer to Headers', (done) => { - inject([HttpClient, HttpTestingController], - (http: HttpClient, mock: HttpTestingController) => { - - service.setToken('jwt-token'); - http.get('/api').subscribe(response => { - expect(response).toBeTruthy(); - done(); - }); - const request = mock.expectOne(req => (req.headers.has('X-Jwt-Token'))); - - request.flush({data: 'test'}, {headers: new HttpHeaders({'X-Jwt-Token': 'tokenos'})}); - mock.verify(); - })(); - }); - afterEach(inject([HttpTestingController], (mock: HttpTestingController) => { - mock.verify(); - TestBed.resetTestingModule(); - })); - }); - - afterEach(() => { - TestBed.resetTestingModule(); - }); -}); +// import {inject, TestBed} from '@angular/core/testing'; +// import {ConfigurationService} from '../../configuration/configuration.service'; +// import {TestConfigurationService} from '../../utility/tests/test-config'; +// import {HTTP_INTERCEPTORS, HttpClient, HttpHeaders} from '@angular/common/http'; +// import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +// import {RouterTestingModule} from '@angular/router/testing'; +// import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +// import {LoggerService} from '../../logger/services/logger.service'; +// import {AnonymousService} from '../anonymous/anonymous.service'; +// import {AnonymousAuthenticationInterceptor} from './anonymous-authentication-interceptor'; +// +// describe('AnonymousAuthenticationInterceptor', () => { +// let service: AnonymousService; +// let warnSpy: jasmine.Spy; +// +// beforeEach(() => { +// TestBed.configureTestingModule({ +// imports: [HttpClientTestingModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])], +// providers: [ +// {provide: ConfigurationService, useClass: TestConfigurationService}, +// AnonymousService, +// { +// provide: HTTP_INTERCEPTORS, +// useClass: AnonymousAuthenticationInterceptor, +// multi: true +// } +// ] +// }); +// service = TestBed.inject(AnonymousService); +// warnSpy = spyOn(TestBed.inject(LoggerService), 'warn'); +// }); +// +// describe('intercept HTTP request', () => { +// it('should add JWT bearer to Headers', (done) => { +// inject([HttpClient, HttpTestingController], +// (http: HttpClient, mock: HttpTestingController) => { +// +// service.setToken('jwt-token'); +// http.get('/api').subscribe(response => { +// expect(response).toBeTruthy(); +// done(); +// }); +// const request = mock.expectOne(req => (req.headers.has('X-Jwt-Token'))); +// +// request.flush({data: 'test'}, {headers: new HttpHeaders({'X-Jwt-Token': 'tokenos'})}); +// mock.verify(); +// })(); +// }); +// afterEach(inject([HttpTestingController], (mock: HttpTestingController) => { +// mock.verify(); +// TestBed.resetTestingModule(); +// })); +// }); +// +// afterEach(() => { +// TestBed.resetTestingModule(); +// }); +// }); diff --git a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts index 91f79c14ee..5dcdc57e17 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts @@ -1,41 +1,41 @@ -import {Injectable} from '@angular/core'; -import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; -import {Observable, throwError} from 'rxjs'; -import {catchError, tap} from 'rxjs/operators'; -import {AnonymousService} from '../anonymous/anonymous.service'; - -@Injectable() -export class AnonymousAuthenticationInterceptor implements HttpInterceptor { - - constructor(protected _anonymousService: AnonymousService) {} - - intercept(req: HttpRequest, next: HttpHandler): Observable> { - const xAnonymousToken = this._anonymousService.getToken(); - - if (!this._anonymousService) { - next.handle(req); - } - - if (!!xAnonymousToken) { - req = req.clone({ - headers: req.headers.set(this._anonymousService.anonymousTokenHeader, xAnonymousToken) - }); - } - return next.handle(req).pipe( - tap(event => { - if (event instanceof HttpResponse) { - if (event.headers.has(this._anonymousService.anonymousTokenHeader)) { - this._anonymousService.setToken(event.headers.get(this._anonymousService.anonymousTokenHeader)); - } - } - }), - catchError(errorEvent => { - if (errorEvent instanceof HttpErrorResponse && errorEvent.status === 401) { - console.debug('Authentication token is invalid. Clearing session token'); - this._anonymousService.removeToken(); - } - return throwError(errorEvent); - }) - ); - } -} +// import {Injectable} from '@angular/core'; +// import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; +// import {Observable, throwError} from 'rxjs'; +// import {catchError, tap} from 'rxjs/operators'; +// import {AnonymousService} from '../anonymous/anonymous.service'; +// +// @Injectable() +// export class AnonymousAuthenticationInterceptor implements HttpInterceptor { +// +// constructor(protected _anonymousService: AnonymousService) {} +// +// intercept(req: HttpRequest, next: HttpHandler): Observable> { +// const xAnonymousToken = this._anonymousService.getToken(); +// +// if (!this._anonymousService) { +// next.handle(req); +// } +// +// if (!!xAnonymousToken) { +// req = req.clone({ +// headers: req.headers.set(this._anonymousService.anonymousTokenHeader, xAnonymousToken) +// }); +// } +// return next.handle(req).pipe( +// tap(event => { +// if (event instanceof HttpResponse) { +// if (event.headers.has(this._anonymousService.anonymousTokenHeader)) { +// this._anonymousService.setToken(event.headers.get(this._anonymousService.anonymousTokenHeader)); +// } +// } +// }), +// catchError(errorEvent => { +// if (errorEvent instanceof HttpErrorResponse && errorEvent.status === 401) { +// console.debug('Authentication token is invalid. Clearing session token'); +// this._anonymousService.removeToken(); +// } +// return throwError(errorEvent); +// }) +// ); +// } +// } diff --git a/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts b/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts index ba254dd3d8..3e90a7f9b1 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts @@ -11,7 +11,7 @@ import {Observable, throwError} from 'rxjs'; import {catchError, tap} from 'rxjs/operators'; import {SessionService} from '../session/services/session.service'; import {RedirectService} from '../../routing/redirect-service/redirect.service'; -import {AnonymousService} from '../anonymous/anonymous.service'; +// import {AnonymousService} from '../anonymous/anonymous.service'; import {SessionIdleTimerService} from "../session/services/session-idle-timer.service"; @Injectable() @@ -19,7 +19,7 @@ export class AuthenticationInterceptor implements HttpInterceptor { constructor(private _session: SessionService, private _redirect: RedirectService, - private _anonymousService: AnonymousService, + // private _anonymousService: AnonymousService, private idleTimerService: SessionIdleTimerService) { } @@ -37,7 +37,7 @@ export class AuthenticationInterceptor implements HttpInterceptor { return next.handle(req).pipe( tap(event => { if (event instanceof HttpResponse) { - if (event.headers.has(this._session.sessionHeader) && !event.headers.has(this._anonymousService.anonymousTokenHeader)) { + if (event.headers.has(this._session.sessionHeader)) { this._session.setVerifiedToken(event.headers.get(this._session.sessionHeader)); } } diff --git a/projects/netgrif-components-core/src/lib/filter/models/filter.ts b/projects/netgrif-components-core/src/lib/filter/models/filter.ts index 9d26bdf1f8..74c4a722aa 100644 --- a/projects/netgrif-components-core/src/lib/filter/models/filter.ts +++ b/projects/netgrif-components-core/src/lib/filter/models/filter.ts @@ -71,6 +71,16 @@ export abstract class Filter { */ public abstract bodyContainsQuery(): boolean; + + /** + * Checks whether any of the filter bodies contains the `caseId` attribute. + * + * This method analyzes the body of the filter to determine if at least one of its parts includes a `caseId` field. + * + * @returns `true` if the `caseId` attribute exists in any of the filter bodies; otherwise, `false`. + */ + public abstract bodyContainsCaseId(): boolean; + /** * Returns the necessary request params for the filter. Default implementation returns an empty object. * The params are added on top of the request when sending it to the backend by the respective service methods. diff --git a/projects/netgrif-components-core/src/lib/filter/models/merged-filter.ts b/projects/netgrif-components-core/src/lib/filter/models/merged-filter.ts index 23a952c801..6118f49586 100644 --- a/projects/netgrif-components-core/src/lib/filter/models/merged-filter.ts +++ b/projects/netgrif-components-core/src/lib/filter/models/merged-filter.ts @@ -121,6 +121,13 @@ export class MergedFilter extends Filter { return this._filters.some(f => f.query !== undefined && f.query !== null); } + /** + * See [Filter.bodyContainsCaseId()]{@link Filter#bodyContainsCaseId} + */ + bodyContainsCaseId(): boolean { + return false; + } + /** * Returns the necessary request params for the filter. * @returns params with `operation` set to either `AND` or `OR` based on this object's `_operator` property. diff --git a/projects/netgrif-components-core/src/lib/filter/models/simple-filter.ts b/projects/netgrif-components-core/src/lib/filter/models/simple-filter.ts index 557d3d9e97..5d3e9cb447 100644 --- a/projects/netgrif-components-core/src/lib/filter/models/simple-filter.ts +++ b/projects/netgrif-components-core/src/lib/filter/models/simple-filter.ts @@ -102,6 +102,20 @@ export class SimpleFilter extends Filter { return this._filter.query !== undefined && this._filter.query !== null; } + /** + * See [Filter.bodyContainsCaseId()]{@link Filter#bodyContainsCaseId} + */ + bodyContainsCaseId(): boolean { + return !!this._filter + && !!this._filter['case'] + && ((!Array.isArray(this._filter['case']) + && !!this._filter['case'].id + && this._filter['case'].id.length > 0) + || (Array.isArray(this._filter['case']) + && this._filter['case'].length > 0)); + } + + /** * See [Filter.getRequestBody()]{@link Filter#getRequestBody} */ diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts index 1ed4b54e37..62dfe07298 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts @@ -396,9 +396,9 @@ class TestUserService extends UserService { userTransform: UserTransformer, log: LoggerService, session: SessionService, - anonymousService: AnonymousService, + // anonymousService: AnonymousService, config: ConfigurationService) { - super(authService, userResource, userTransform, log, session, anonymousService, config); + super(authService, userResource, userTransform, log, session, config); } public setUser(user: User) { diff --git a/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts index f386515f56..6e56b015fa 100644 --- a/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts @@ -1,16 +1,6 @@ - import { CaseResourceService } from '../../resources/engine-endpoint/case-resource.service'; -import { ResourceProvider } from '../../resources/resource-provider.service'; -import { ConfigurationService } from '../../configuration/configuration.service'; export const CaseResourceServiceProvider = { provide: CaseResourceService, - useFactory: (provider: ResourceProvider, - config: ConfigurationService) => { - return new CaseResourceService(provider, config); - }, - deps: [ - ResourceProvider, - ConfigurationService, - ] + useClass: CaseResourceService } diff --git a/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts index 645e4fc9a3..69c70d8b18 100644 --- a/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts @@ -1,14 +1,6 @@ import { PetriNetResourceService } from '../../resources/engine-endpoint/petri-net-resource.service'; -import { ResourceProvider } from '../../resources/resource-provider.service'; -import { ConfigurationService } from '../../configuration/configuration.service'; export const PetriNetResourceServiceProvider = { provide: PetriNetResourceService, - useFactory: (provider: ResourceProvider, - config: ConfigurationService) => { - return new PetriNetResourceService(provider, config); - }, - deps: [ - ResourceProvider, - ConfigurationService] + useClass: PetriNetResourceService } diff --git a/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts index 22142222ae..45553ff727 100644 --- a/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts @@ -1,15 +1,6 @@ import { ProcessService } from '../../process/process.service'; -import { PetriNetResourceService } from '../../resources/engine-endpoint/petri-net-resource.service'; -import { LoggerService } from '../../logger/services/logger.service'; export const ProcessServiceProvider = { provide: ProcessService, - useFactory: (petriNetResource: PetriNetResourceService, - loggerService: LoggerService) => { - return new ProcessService(petriNetResource, loggerService); - }, - deps: [ - PetriNetResourceService, - LoggerService - ] + useClass: ProcessService } diff --git a/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts index 9a7ba24514..d4bfa1de75 100644 --- a/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts @@ -1,22 +1,6 @@ import { TaskResourceService } from '../../resources/engine-endpoint/task-resource.service'; -import { LoggerService } from '../../logger/services/logger.service'; -import { ResourceProvider } from '../../resources/resource-provider.service'; -import { ConfigurationService } from '../../configuration/configuration.service'; -import { FieldConverterService } from '../../task-content/services/field-converter.service'; export const TaskResourceServiceProvider = { provide: TaskResourceService, - useFactory: ( - logger: LoggerService, - provider: ResourceProvider, - config: ConfigurationService, - fieldConverter: FieldConverterService) => { - return new TaskResourceService(provider, config, fieldConverter, logger); - }, - deps: [ - LoggerService, - ResourceProvider, - ConfigurationService, - FieldConverterService - ] + useClass: TaskResourceService } diff --git a/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts index 66e0be11ad..03270735ce 100644 --- a/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts @@ -3,6 +3,7 @@ import {ConfigurationService} from '../../configuration/configuration.service'; import {Page} from '../interface/page'; import {Pagination} from '../interface/pagination'; import {PaginationParams} from '../../utility/pagination/pagination-params'; +import {User} from "../../user/models/user"; /** * The class that contains behavior common to all resource services. @@ -123,7 +124,14 @@ export abstract class AbstractResourceService { }; } - protected resolvePublicUrl(baseUrl: string, isUserAnonymous: boolean): string { - return isUserAnonymous ? baseUrl : baseUrl + '/public'; + protected resolvePublicEndpoint(endpoint: string, user: User): string { + if (!!user && user.isAnonymous()) { + if (endpoint.includes('/')) { + const slashIndex = endpoint.indexOf('/'); + return endpoint.replace(endpoint.substring(slashIndex, slashIndex + 1), '/public/'); + } + return endpoint + '/public'; + } + return endpoint; } } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts index 240091e3fb..c65ae941a6 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts @@ -98,7 +98,7 @@ export class CaseResourceService extends AbstractResourceService implements Coun * {{baseUrl}}/api/workflow/case */ public createCase(body: CreateCaseRequestBody): Observable { - return this._resourceProvider.post$('workflow/case/', this.SERVER_URL, body).pipe(map(r => this.changeType(r, undefined))); + return this._resourceProvider.post$(this.resolvePublicEndpoint('workflow/case/', this.userService.user), this.SERVER_URL, body).pipe(map(r => this.changeType(r, undefined))); } /** diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts index 292162c6ac..06c28a93da 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts @@ -23,7 +23,7 @@ export class PetriNetResourceService extends AbstractResourceService { constructor(provider: ResourceProvider, configService: ConfigurationService, - protected userService: UserService) { + protected userService: UserService,) { super('petrinet', provider, configService); } @@ -48,7 +48,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/data */ public getDataPetriNet(body: object): Observable { // TODO: response - return this._resourceProvider.post$(this.resolvePublicUrl('petrinet', this.userService.user.isAnonymous()) + '/data', this.SERVER_URL, body) + return this._resourceProvider.post$(this.resolvePublicEndpoint('petrinet', this.userService.user) + '/data', this.SERVER_URL, body) .pipe(map(r => this.changeType(r, undefined))); } @@ -60,7 +60,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/transitions */ public getPetriNetTransitions(netId: string): Observable> { - return this._resourceProvider.get$(this.resolvePublicUrl('petrinet', this.userService.user.isAnonymous()) + '/transitions', this.SERVER_URL, new HttpParams().set('ids', netId)) + return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/transitions', this.userService.user), this.SERVER_URL, new HttpParams().set('ids', netId)) .pipe(map(r => this.changeType(r, 'transitionReferences'))); } @@ -72,7 +72,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id}/transactions */ public getPetriNetTransactions(netId: string, params?: Params): Observable> { - return this._resourceProvider.get$(this.resolvePublicUrl('petrinet', this.userService.user.isAnonymous()) + netId + '/transactions', this.SERVER_URL, params) + return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + netId + '/transactions', this.userService.user), this.SERVER_URL, params) .pipe(map(r => this.changeType(r, 'transactions'))); } @@ -84,7 +84,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id}/roles */ public getPetriNetRoles(netId: string, params?: Params): Observable { - return this._resourceProvider.get$('/petrinet/' + netId + '/roles', this.SERVER_URL, params) + return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + netId + '/roles', this.userService.user), this.SERVER_URL, params) .pipe(map(r => this.changeType(r, undefined))); } @@ -119,7 +119,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{identifier}/{version} */ public getOne(identifier: string, version: string, params?: Params): Observable { - return this._resourceProvider.get$('petrinet/' + btoa(identifier) + '/' + version, this.SERVER_URL, params) + return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + btoa(identifier) + '/' + version, this.userService.user), this.SERVER_URL, params) .pipe(map(r => this.changeType(r, 'petriNetReferences'))); } @@ -131,7 +131,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id} */ public getOneById(netId: string, params?: Params): Observable { - return this._resourceProvider.get$('petrinet/' + netId, this.SERVER_URL, params) + return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + netId, this.userService.user), this.SERVER_URL, params) .pipe(map(r => this.changeType(r, undefined))); } @@ -168,7 +168,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/search */ public searchPetriNets(body: PetriNetRequestBody, params?: Params): Observable> { - return this._resourceProvider.post$('petrinet/search', this.SERVER_URL, body, params) + return this._resourceProvider.post$(this.resolvePublicEndpoint('petrinet/search', this.userService.user), this.SERVER_URL, body, params) // .pipe(map(r => this.getResourcePage(r, 'petriNetReferences'))); .pipe(map(r => this.mapToPage(r))); } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.spec.ts index 6e55219f34..99aa7e1888 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.spec.ts @@ -153,8 +153,8 @@ describe('TaskResourceService', () => { it('should getAllTasksByCases', (done) => { inject([HttpTestingController], (httpMock: HttpTestingController) => { - service.getAllTasksByCases({}).subscribe(res => { - expect(res.length).toEqual(0); + service.getAllTasksByCases([]).subscribe(res => { + expect(res.content.length).toEqual(0); done(); }); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts index d7e639eae6..96645c6f78 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts @@ -64,7 +64,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/assign/:id public assignTask(taskId: string): Observable { - return this._resourceProvider.get$('task/assign/' + taskId, this.SERVER_URL) + return this._resourceProvider.get$(this.resolvePublicEndpoint('task/assign/' + taskId, this.userService.user), this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -74,7 +74,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/cancel/:id public cancelTask(taskId: string): Observable { - return this._resourceProvider.get$('task/cancel/' + taskId, this.SERVER_URL) + return this._resourceProvider.get$(this.resolvePublicEndpoint( 'task/cancel/' + taskId, this.userService.user), this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -94,7 +94,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/finish/:id public finishTask(taskId: string): Observable { - return this._resourceProvider.get$('task/finish/' + taskId, this.SERVER_URL) + return this._resourceProvider.get$(this.resolvePublicEndpoint('task/finish/' + taskId, this.userService.user), this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -143,9 +143,9 @@ export class TaskResourceService extends AbstractResourceService implements Coun * POST */ // {{baseUrl}}/api/task/case - public getAllTasksByCases(body: object): Observable> { // TODO: ?? - return this._resourceProvider.post$('task/case', this.SERVER_URL, body) - .pipe(map(r => this.changeType(r, 'tasks'))); + public getAllTasksByCases(caseIds: string[]): Observable> { + return this._resourceProvider.post$(this.resolvePublicEndpoint('task/case', this.userService.user), this.SERVER_URL, caseIds) + .pipe(map(r => this.getResourcePage(r, 'tasks'))); } /** @@ -154,7 +154,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/case/:id public getAllTasksByCase(caseId: string): Observable> { - return this._resourceProvider.get$('task/case/' + caseId, this.SERVER_URL) + return this._resourceProvider.get$(this.resolvePublicEndpoint('task/case/' + caseId, this.userService.user), this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -190,7 +190,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/:id/data public rawGetData(taskId: string): Observable { - return this._resourceProvider.get$('task/' + taskId + '/data', this.SERVER_URL) + return this._resourceProvider.get$(this.resolvePublicEndpoint('task/' + taskId + '/data', this.userService.user), this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -256,7 +256,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/:id/data public setData(taskId: string, body: TaskSetDataRequestBody): Observable { - return this._resourceProvider.post$('task/' + taskId + '/data', this.SERVER_URL, body) + return this._resourceProvider.post$(this.resolvePublicEndpoint('task/' + taskId + '/data', this.userService.user), this.SERVER_URL, body) .pipe(map(r => this.changeType(r, undefined))); } @@ -268,7 +268,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun // {{baseUrl}}/api/task/:id/file/:field - for file field // {{baseUrl}}/api/task/:id/file/:field/:name - for file list field public downloadFile(taskId: string, params: HttpParams): Observable { - const url = `task/${taskId}/file${params?.has("fileName") ? '/named' : ''}`; + const url = this.resolvePublicEndpoint(`task/${taskId}/file${params?.has("fileName") ? '/named' : ''}`, this.userService.user); return this._resourceProvider.getBlob$(url, this.SERVER_URL, params).pipe( map(event => { switch (event.type) { @@ -292,7 +292,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun // {{baseUrl}}/api/task/:id/files/:field - for file list field public uploadFile(taskId: string, body: object, multipleFiles: boolean): Observable { - const url = `task/${taskId}/${multipleFiles ? 'files' : 'file'}`; + const url = this.resolvePublicEndpoint(`task/${taskId}/${multipleFiles ? 'files' : 'file'}`, this.userService.user); return this._resourceProvider.postWithEvent$(url, this.SERVER_URL, body).pipe( map(event => { switch (event.type) { @@ -313,8 +313,8 @@ export class TaskResourceService extends AbstractResourceService implements Coun * DELETE */ public deleteFile(taskId: string, body?: FileFieldRequest): Observable { - const url = `task/${taskId}/file${body?.fileName ? '/named' : ''}`; - return this._resourceProvider.delete$(url, this.SERVER_URL, {}, {}, 'json', body).pipe( + const url = this.resolvePublicEndpoint(`task/${taskId}/file${body?.fileName ? '/named' : ''}`, this.userService.user); + return this._resourceProvider.delete$(url , this.SERVER_URL, {}, {}, 'json', body).pipe( map(r => this.changeType(r, undefined)) ); } diff --git a/projects/netgrif-components-core/src/lib/task/services/assign-policy.service.ts b/projects/netgrif-components-core/src/lib/task/services/assign-policy.service.ts index 87a9d0444a..47111eabed 100644 --- a/projects/netgrif-components-core/src/lib/task/services/assign-policy.service.ts +++ b/projects/netgrif-components-core/src/lib/task/services/assign-policy.service.ts @@ -55,11 +55,8 @@ export class AssignPolicyService extends TaskHandlingService { if (!this._userService.isCurrentUserEmpty()) { this.performAssign(taskOpened, afterAction); } else { - race([ - this._userService.anonymousUser$, - this._userService.user$ - ]) - .pipe(filter(user => !this._userService.isUserEmpty(user))) + // this._userService.anonymousUser$, + this._userService.user$.pipe(filter(user => !this._userService.isUserEmpty(user))) .pipe(take(1)) .subscribe(user => this.performAssign(taskOpened, afterAction)); } diff --git a/projects/netgrif-components-core/src/lib/user/models/user.ts b/projects/netgrif-components-core/src/lib/user/models/user.ts index 6ce74f7918..bbc6c71fe3 100644 --- a/projects/netgrif-components-core/src/lib/user/models/user.ts +++ b/projects/netgrif-components-core/src/lib/user/models/user.ts @@ -61,7 +61,7 @@ export class User implements IUser { } public isAnonymous(): boolean { - return this.authorities.length === 1 && this.authorities[0] === 'ANONYMOUS_USER'; + return this.authorities.length === 1 && this.authorities[0].includes('ANONYMOUS'); } } diff --git a/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts b/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts index c2b9f8201e..f44cf527c9 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts @@ -19,9 +19,9 @@ export class UserPreferenceService implements OnDestroy { protected _preferences: Preferences; protected _preferencesChanged$: Subject; protected _sub: Subscription; - protected _subAnonym: Subscription; + // protected _subAnonym: Subscription; public _drawerWidthChanged$: Subject; - protected _anonym: boolean; + // protected _anonym: boolean; constructor(protected _userService: UserService, protected _userResourceService: UserResourceService, @@ -31,7 +31,7 @@ export class UserPreferenceService implements OnDestroy { this._preferences = this._emptyPreferences(); this._preferencesChanged$ = new Subject(); this._drawerWidthChanged$ = new Subject(); - this._anonym = false; + // this._anonym = false; this._sub = this._userService.user$.subscribe(loggedUser => { if (loggedUser && loggedUser.id !== '') { @@ -47,21 +47,21 @@ export class UserPreferenceService implements OnDestroy { } }); - this._subAnonym = this._userService.anonymousUser$.subscribe(loggedUser => { - if (loggedUser && loggedUser.id !== '') { - this._userResourceService.getPublicPreferences().subscribe(prefs => { - this._preferences = this._emptyPreferences(); - Object.assign(this._preferences, prefs); - this._preferencesChanged$.next(); - this._anonym = true; - } - ); - } else { - this._preferences = this._emptyPreferences(); - this._preferencesChanged$.next(); - this._anonym = false; - } - }); + // this._subAnonym = this._userService.anonymousUser$.subscribe(loggedUser => { + // if (loggedUser && loggedUser.id !== '') { + // this._userResourceService.getPublicPreferences().subscribe(prefs => { + // this._preferences = this._emptyPreferences(); + // Object.assign(this._preferences, prefs); + // this._preferencesChanged$.next(); + // this._anonym = true; + // } + // ); + // } else { + // this._preferences = this._emptyPreferences(); + // this._preferencesChanged$.next(); + // this._anonym = false; + // } + // }); this._drawerWidthChanged$.asObservable().pipe( debounceTime(DRAWER_DEBOUNCE) @@ -125,15 +125,15 @@ export class UserPreferenceService implements OnDestroy { } protected _savePreferences(): void { - if (!this._anonym) { + // if (!this._anonym) { this._userResourceService.setPreferences(this._preferences).subscribe(resultMessage => { this.resultMessage(resultMessage); }); - } else { - this._userResourceService.setPublicPreferences(this._preferences).subscribe(resultMessage => { - this.resultMessage(resultMessage); - }); - } + // } else { + // this._userResourceService.setPublicPreferences(this._preferences).subscribe(resultMessage => { + // this.resultMessage(resultMessage); + // }); + // } } protected resultMessage(resultMessage): void { diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.ts index 2ef8a10529..28c2544188 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.ts @@ -22,10 +22,10 @@ export class UserService implements OnDestroy { protected _user: User; protected _userChange$: ReplaySubject; - protected _anonymousUserChange$: ReplaySubject; + // protected _anonymousUserChange$: ReplaySubject; protected _loginCalled: boolean; protected _subAuth: Subscription; - protected _subAnonym: Subscription; + // protected _subAnonym: Subscription; private _publicLoadCalled: boolean; public readonly GLOBAL_ROLE_PREFIX = 'global_'; @@ -35,12 +35,12 @@ export class UserService implements OnDestroy { protected _userTransform: UserTransformer, protected _log: LoggerService, protected _session: SessionService, - protected _anonymousService: AnonymousService, + // protected _anonymousService: AnonymousService, protected _config: ConfigurationService) { this._user = this.emptyUser(); this._loginCalled = false; this._userChange$ = new ReplaySubject(1); - this._anonymousUserChange$ = new ReplaySubject(1); + // this._anonymousUserChange$ = new ReplaySubject(1); this._config.loaded$ .pipe( filter(loaded => loaded), @@ -56,14 +56,14 @@ export class UserService implements OnDestroy { } }); }); - this._subAnonym = this._anonymousService.tokenSet.subscribe(token => { - if (token) { - this.loadPublicUser(); - } else { - this.clearUser(); - this.publishAnonymousUserChange(); - } - }); + // this._subAnonym = this._anonymousService.tokenSet.subscribe(token => { + // if (token) { + // this.loadPublicUser(); + // } else { + // this.clearUser(); + // this.publishAnonymousUserChange(); + // } + // }); }); } @@ -79,15 +79,15 @@ export class UserService implements OnDestroy { return this.anonymousUser; } - get anonymousUser$(): Observable { - return this._anonymousUserChange$.asObservable(); - } + // get anonymousUser$(): Observable { + // return this._anonymousUserChange$.asObservable(); + // } ngOnDestroy(): void { this._userChange$.complete(); - this._anonymousUserChange$.complete(); + // this._anonymousUserChange$.complete(); this._subAuth.unsubscribe(); - this._subAnonym.unsubscribe(); + // this._subAnonym.unsubscribe(); } /** @@ -186,7 +186,7 @@ export class UserService implements OnDestroy { } protected emptyUser() { - return new User('', '', '', '', '', '', [], [], [], []); + return new User('', '', '', '', '', '', ['ANONYMOUS_USER'], [], [], []); } protected loadUser(): void { @@ -206,18 +206,18 @@ export class UserService implements OnDestroy { }); } - public loadPublicUser(): void { - this._userResource.getPublicLoggedUser().pipe(take(1)).subscribe((user: UserResource) => { - if (user) { - const backendUser = {...user, id: user.id.toString()}; - this._user = this._userTransform.transform(backendUser); - this.publishAnonymousUserChange(); - } - }, error => { - this._log.error('Loading logged user has failed! Initialisation has not be completed successfully!', error); - this._publicLoadCalled = false; - }); - } + // public loadPublicUser(): void { + // this._userResource.getPublicLoggedUser().pipe(take(1)).subscribe((user: UserResource) => { + // if (user) { + // const backendUser = {...user, id: user.id.toString()}; + // this._user = this._userTransform.transform(backendUser); + // // this.publishAnonymousUserChange(); + // } + // }, error => { + // this._log.error('Loading logged user has failed! Initialisation has not be completed successfully!', error); + // this._publicLoadCalled = false; + // }); + // } public clearUser() { this._user = this.emptyUser(); @@ -235,7 +235,7 @@ export class UserService implements OnDestroy { this._userChange$.next(this.user); } - protected publishAnonymousUserChange(): void { - this._anonymousUserChange$.next(this.user); - } + // protected publishAnonymousUserChange(): void { + // this._anonymousUserChange$.next(this.user); + // } } diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts index 77e251da3d..7d4beba746 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts @@ -222,7 +222,10 @@ export class TaskViewService extends AbstractSortableViewComponent implements On this._loading$.on(requestContext.filter); let request: Observable>; - if (requestContext.filter.bodyContainsQuery() || this._preferredEndpoint === TaskEndpoint.ELASTIC) { + if (this._userService.user.isAnonymous()) { + const caseIds = Array.isArray(requestContext.filter.getRequestBody()['case']) ? requestContext.filter.getRequestBody()['case'].map(aCase => aCase.id) : [requestContext.filter.getRequestBody()['case'].id]; + request = this._taskService.getAllTasksByCases(caseIds).pipe(take(1)); + } else if (requestContext.filter.bodyContainsQuery() || this._preferredEndpoint === TaskEndpoint.ELASTIC) { request = timer(200).pipe( switchMap(() => this._taskService.searchTask(requestContext.filter, params).pipe(take(1))) ); From ffb3b5eb6cbf4839d6da975a72a76bebc41124fa Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 3 Feb 2026 13:25:00 +0100 Subject: [PATCH 04/16] [NAE-2241] Anonymous access Changed the default role from 'ANONYMOUS_USER' to 'ANONYMOUS' in the `emptyUser` function for consistency. Adjusted the task view service to correctly retrieve the first case ID from the request body when handling anonymous users. --- .../src/lib/user/services/user.service.ts | 2 +- .../src/lib/view/task-view/service/task-view.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.ts index 28c2544188..5e14f27124 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.ts @@ -186,7 +186,7 @@ export class UserService implements OnDestroy { } protected emptyUser() { - return new User('', '', '', '', '', '', ['ANONYMOUS_USER'], [], [], []); + return new User('', '', '', '', '', '', ['ANONYMOUS'], [], [], []); } protected loadUser(): void { diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts index 7d4beba746..2598bf0571 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts @@ -223,7 +223,7 @@ export class TaskViewService extends AbstractSortableViewComponent implements On let request: Observable>; if (this._userService.user.isAnonymous()) { - const caseIds = Array.isArray(requestContext.filter.getRequestBody()['case']) ? requestContext.filter.getRequestBody()['case'].map(aCase => aCase.id) : [requestContext.filter.getRequestBody()['case'].id]; + const caseIds = Array.isArray(requestContext.filter.getRequestBody()['case']) ? requestContext.filter.getRequestBody()['case'][0].id : [requestContext.filter.getRequestBody()['case'].id]; request = this._taskService.getAllTasksByCases(caseIds).pipe(take(1)); } else if (requestContext.filter.bodyContainsQuery() || this._preferredEndpoint === TaskEndpoint.ELASTIC) { request = timer(200).pipe( From 44be3b67e81654986779a3eee7468280bdabfcfb Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 3 Feb 2026 13:35:06 +0100 Subject: [PATCH 05/16] [NAE-2241] Anonymous access refactor Deleted the AnonymousService, its tests, and all related references, including code for anonymous user handling and preferences. Updated configurations and removed unused or commented-out code across multiple files. --- nae.json | 18 +++--- .../anonymous/anonymous.service.spec.ts | 28 -------- .../anonymous/anonymous.service.ts | 64 ------------------- ...onymous-authentication-interceptor.spec.ts | 58 ----------------- .../anonymous-authentication-interceptor.ts | 41 ------------ .../lib/groups/services/next-group.service.ts | 2 - ...abstract-navigation-tree.component.spec.ts | 1 - .../user/services/user-preference.service.ts | 31 +-------- .../src/lib/user/services/user.service.ts | 36 ----------- 9 files changed, 12 insertions(+), 267 deletions(-) delete mode 100644 projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts delete mode 100644 projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts delete mode 100644 projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts delete mode 100644 projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts diff --git a/nae.json b/nae.json index 16dc91b4ba..02c08c4b8e 100644 --- a/nae.json +++ b/nae.json @@ -3,8 +3,8 @@ "extends": "nae-default", "providers": { "auth": { - "address": "http://localhost:8080/api/", - "authentication": "Basic", + "address": "http://localhost:8800/api/", + "authentication": "BasicWithRealm", "endpoints": { "login": "auth/login", "logout": "auth/logout", @@ -31,37 +31,37 @@ "resources": [ { "name": "case", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" }, { "name": "task", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" }, { "name": "petrinet", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" }, { "name": "user", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" }, { "name": "dashboard", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" }, { "name": "filter", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" }, { "name": "impersonation", - "address": "http://localhost:8080/api/", + "address": "http://localhost:8800/api/", "format": "json" } ] diff --git a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts b/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts deleted file mode 100644 index c0031c9334..0000000000 --- a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -// import {TestBed} from '@angular/core/testing'; -// import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -// import {HttpClientTestingModule} from '@angular/common/http/testing'; -// import {RouterTestingModule} from '@angular/router/testing'; -// import {AnonymousService} from './anonymous.service'; -// import {ConfigurationService} from '../../configuration/configuration.service'; -// import {TestConfigurationService} from '../../utility/tests/test-config'; -// -// describe('AnonymousService', () => { -// let service: AnonymousService; -// -// beforeEach(() => { -// TestBed.configureTestingModule({ -// imports: [NoopAnimationsModule, HttpClientTestingModule, RouterTestingModule.withRoutes([])], -// providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] -// }); -// service = TestBed.inject(AnonymousService); -// }); -// -// it('should be created', () => { -// expect(service).toBeTruthy(); -// }); -// -// afterEach(() => { -// TestBed.resetTestingModule(); -// }); -// }); -// diff --git a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts b/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts deleted file mode 100644 index 1e69e625ee..0000000000 --- a/projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {Injectable, OnDestroy} from '@angular/core'; -import {ConfigurationService} from '../../configuration/configuration.service'; -import {NullStorage} from '../session/null-storage'; -import {BehaviorSubject, Observable} from 'rxjs'; - - -@Injectable({ - providedIn: 'root' -}) -export class AnonymousService implements OnDestroy { - - public static readonly X_ANONYMOUS_TOKEN = 'X-Anonymous-Token'; - protected readonly _anonymousTokenHeader: string; - protected _storage: Storage; - protected _tokenSet: BehaviorSubject; - - constructor(protected _config: ConfigurationService) { - this._anonymousTokenHeader = this._config.get().providers.auth.anonymous ? - this._config.get().providers.auth.anonymous : AnonymousService.X_ANONYMOUS_TOKEN; - this._storage = this.resolveStorage(this._config.get().providers.auth['local']); - this._tokenSet = new BehaviorSubject(false); - } - - get anonymousTokenHeader(): string { - return this._anonymousTokenHeader; - } - - get tokenSet(): Observable { - return this._tokenSet.asObservable(); - } - - public getToken(): string { - return this._storage.getItem(this._anonymousTokenHeader); - } - - public setToken(token: string): void { - this._storage.setItem(this._anonymousTokenHeader, token); - if (!this._tokenSet.getValue()) - this._tokenSet.next(true); - } - - public removeToken(): void { - this._storage.removeItem(this._anonymousTokenHeader); - this._tokenSet.next(false); - } - - ngOnDestroy(): void { - localStorage.removeItem(this._anonymousTokenHeader); - this._tokenSet.complete(); - } - - protected resolveStorage(storage: string): any { - switch (storage) { - case 'local': - return localStorage; - case 'session': - return sessionStorage; - case 'null': - return new NullStorage(); - default: - return localStorage; - } - } -} diff --git a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts deleted file mode 100644 index 220bfa99fb..0000000000 --- a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -// import {inject, TestBed} from '@angular/core/testing'; -// import {ConfigurationService} from '../../configuration/configuration.service'; -// import {TestConfigurationService} from '../../utility/tests/test-config'; -// import {HTTP_INTERCEPTORS, HttpClient, HttpHeaders} from '@angular/common/http'; -// import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; -// import {RouterTestingModule} from '@angular/router/testing'; -// import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -// import {LoggerService} from '../../logger/services/logger.service'; -// import {AnonymousService} from '../anonymous/anonymous.service'; -// import {AnonymousAuthenticationInterceptor} from './anonymous-authentication-interceptor'; -// -// describe('AnonymousAuthenticationInterceptor', () => { -// let service: AnonymousService; -// let warnSpy: jasmine.Spy; -// -// beforeEach(() => { -// TestBed.configureTestingModule({ -// imports: [HttpClientTestingModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])], -// providers: [ -// {provide: ConfigurationService, useClass: TestConfigurationService}, -// AnonymousService, -// { -// provide: HTTP_INTERCEPTORS, -// useClass: AnonymousAuthenticationInterceptor, -// multi: true -// } -// ] -// }); -// service = TestBed.inject(AnonymousService); -// warnSpy = spyOn(TestBed.inject(LoggerService), 'warn'); -// }); -// -// describe('intercept HTTP request', () => { -// it('should add JWT bearer to Headers', (done) => { -// inject([HttpClient, HttpTestingController], -// (http: HttpClient, mock: HttpTestingController) => { -// -// service.setToken('jwt-token'); -// http.get('/api').subscribe(response => { -// expect(response).toBeTruthy(); -// done(); -// }); -// const request = mock.expectOne(req => (req.headers.has('X-Jwt-Token'))); -// -// request.flush({data: 'test'}, {headers: new HttpHeaders({'X-Jwt-Token': 'tokenos'})}); -// mock.verify(); -// })(); -// }); -// afterEach(inject([HttpTestingController], (mock: HttpTestingController) => { -// mock.verify(); -// TestBed.resetTestingModule(); -// })); -// }); -// -// afterEach(() => { -// TestBed.resetTestingModule(); -// }); -// }); diff --git a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts b/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts deleted file mode 100644 index 5dcdc57e17..0000000000 --- a/projects/netgrif-components-core/src/lib/authentication/services/anonymous-authentication-interceptor.ts +++ /dev/null @@ -1,41 +0,0 @@ -// import {Injectable} from '@angular/core'; -// import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; -// import {Observable, throwError} from 'rxjs'; -// import {catchError, tap} from 'rxjs/operators'; -// import {AnonymousService} from '../anonymous/anonymous.service'; -// -// @Injectable() -// export class AnonymousAuthenticationInterceptor implements HttpInterceptor { -// -// constructor(protected _anonymousService: AnonymousService) {} -// -// intercept(req: HttpRequest, next: HttpHandler): Observable> { -// const xAnonymousToken = this._anonymousService.getToken(); -// -// if (!this._anonymousService) { -// next.handle(req); -// } -// -// if (!!xAnonymousToken) { -// req = req.clone({ -// headers: req.headers.set(this._anonymousService.anonymousTokenHeader, xAnonymousToken) -// }); -// } -// return next.handle(req).pipe( -// tap(event => { -// if (event instanceof HttpResponse) { -// if (event.headers.has(this._anonymousService.anonymousTokenHeader)) { -// this._anonymousService.setToken(event.headers.get(this._anonymousService.anonymousTokenHeader)); -// } -// } -// }), -// catchError(errorEvent => { -// if (errorEvent instanceof HttpErrorResponse && errorEvent.status === 401) { -// console.debug('Authentication token is invalid. Clearing session token'); -// this._anonymousService.removeToken(); -// } -// return throwError(errorEvent); -// }) -// ); -// } -// } diff --git a/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts b/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts index 7a2af97fec..798da2387d 100644 --- a/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts +++ b/projects/netgrif-components-core/src/lib/groups/services/next-group.service.ts @@ -25,7 +25,6 @@ export class NextGroupService implements OnDestroy { protected _memberGroups$: BehaviorSubject>; protected _ownerGroups$: BehaviorSubject>; - // private _userSub: Subscription; constructor(protected _userService: UserService, protected _caseResourceService: CaseResourceService) { this._ownerGroups$ = new BehaviorSubject>([]); @@ -34,7 +33,6 @@ export class NextGroupService implements OnDestroy { } ngOnDestroy(): void { - // this._userSub.unsubscribe(); this._memberGroups$.complete(); this._ownerGroups$.complete(); } diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts index 62dfe07298..b56d68ead2 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-tree/abstract-navigation-tree.component.spec.ts @@ -22,7 +22,6 @@ import {UserResourceService} from '../../resources/engine-endpoint/user-resource import {UserTransformer} from '../../authentication/models/user.transformer'; import {SessionService} from '../../authentication/session/services/session.service'; import {User} from '../../user/models/user'; -import {AnonymousService} from '../../authentication/anonymous/anonymous.service'; import {ActiveGroupService} from '../../groups/services/active-group.service'; import {TaskResourceService} from '../../resources/engine-endpoint/task-resource.service'; import {LanguageService} from '../../translate/language.service'; diff --git a/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts b/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts index f44cf527c9..f05bcd1012 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user-preference.service.ts @@ -19,9 +19,7 @@ export class UserPreferenceService implements OnDestroy { protected _preferences: Preferences; protected _preferencesChanged$: Subject; protected _sub: Subscription; - // protected _subAnonym: Subscription; public _drawerWidthChanged$: Subject; - // protected _anonym: boolean; constructor(protected _userService: UserService, protected _userResourceService: UserResourceService, @@ -31,7 +29,6 @@ export class UserPreferenceService implements OnDestroy { this._preferences = this._emptyPreferences(); this._preferencesChanged$ = new Subject(); this._drawerWidthChanged$ = new Subject(); - // this._anonym = false; this._sub = this._userService.user$.subscribe(loggedUser => { if (loggedUser && loggedUser.id !== '') { @@ -47,22 +44,6 @@ export class UserPreferenceService implements OnDestroy { } }); - // this._subAnonym = this._userService.anonymousUser$.subscribe(loggedUser => { - // if (loggedUser && loggedUser.id !== '') { - // this._userResourceService.getPublicPreferences().subscribe(prefs => { - // this._preferences = this._emptyPreferences(); - // Object.assign(this._preferences, prefs); - // this._preferencesChanged$.next(); - // this._anonym = true; - // } - // ); - // } else { - // this._preferences = this._emptyPreferences(); - // this._preferencesChanged$.next(); - // this._anonym = false; - // } - // }); - this._drawerWidthChanged$.asObservable().pipe( debounceTime(DRAWER_DEBOUNCE) ).subscribe(newWidth => { @@ -125,15 +106,9 @@ export class UserPreferenceService implements OnDestroy { } protected _savePreferences(): void { - // if (!this._anonym) { - this._userResourceService.setPreferences(this._preferences).subscribe(resultMessage => { - this.resultMessage(resultMessage); - }); - // } else { - // this._userResourceService.setPublicPreferences(this._preferences).subscribe(resultMessage => { - // this.resultMessage(resultMessage); - // }); - // } + this._userResourceService.setPreferences(this._preferences).subscribe(resultMessage => { + this.resultMessage(resultMessage); + }); } protected resultMessage(resultMessage): void { diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.ts index 5e14f27124..d808240288 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.ts @@ -12,7 +12,6 @@ import {LoggerService} from '../../logger/services/logger.service'; import {HttpErrorResponse} from '@angular/common/http'; import {SessionService} from '../../authentication/session/services/session.service'; import {UserResource} from '../../resources/interface/user-resource'; -import {AnonymousService} from '../../authentication/anonymous/anonymous.service'; @Injectable({ @@ -22,11 +21,8 @@ export class UserService implements OnDestroy { protected _user: User; protected _userChange$: ReplaySubject; - // protected _anonymousUserChange$: ReplaySubject; protected _loginCalled: boolean; protected _subAuth: Subscription; - // protected _subAnonym: Subscription; - private _publicLoadCalled: boolean; public readonly GLOBAL_ROLE_PREFIX = 'global_'; @@ -35,12 +31,10 @@ export class UserService implements OnDestroy { protected _userTransform: UserTransformer, protected _log: LoggerService, protected _session: SessionService, - // protected _anonymousService: AnonymousService, protected _config: ConfigurationService) { this._user = this.emptyUser(); this._loginCalled = false; this._userChange$ = new ReplaySubject(1); - // this._anonymousUserChange$ = new ReplaySubject(1); this._config.loaded$ .pipe( filter(loaded => loaded), @@ -56,14 +50,6 @@ export class UserService implements OnDestroy { } }); }); - // this._subAnonym = this._anonymousService.tokenSet.subscribe(token => { - // if (token) { - // this.loadPublicUser(); - // } else { - // this.clearUser(); - // this.publishAnonymousUserChange(); - // } - // }); }); } @@ -79,15 +65,10 @@ export class UserService implements OnDestroy { return this.anonymousUser; } - // get anonymousUser$(): Observable { - // return this._anonymousUserChange$.asObservable(); - // } ngOnDestroy(): void { this._userChange$.complete(); - // this._anonymousUserChange$.complete(); this._subAuth.unsubscribe(); - // this._subAnonym.unsubscribe(); } /** @@ -206,19 +187,6 @@ export class UserService implements OnDestroy { }); } - // public loadPublicUser(): void { - // this._userResource.getPublicLoggedUser().pipe(take(1)).subscribe((user: UserResource) => { - // if (user) { - // const backendUser = {...user, id: user.id.toString()}; - // this._user = this._userTransform.transform(backendUser); - // // this.publishAnonymousUserChange(); - // } - // }, error => { - // this._log.error('Loading logged user has failed! Initialisation has not be completed successfully!', error); - // this._publicLoadCalled = false; - // }); - // } - public clearUser() { this._user = this.emptyUser(); } @@ -234,8 +202,4 @@ export class UserService implements OnDestroy { protected publishUserChange(): void { this._userChange$.next(this.user); } - - // protected publishAnonymousUserChange(): void { - // this._anonymousUserChange$.next(this.user); - // } } From 3949d9370362ab09214e9d055b4ce7d323726a0f Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 4 Feb 2026 09:09:10 +0100 Subject: [PATCH 06/16] [NAE-2241] Anonymous access refactor Deleted the AnonymousService, its tests, and all related references, including code for anonymous user handling and preferences. Updated configurations and removed unused or commented-out code across multiple files. --- .../src/lib/process/public-api.ts | 1 + .../process/public-process.service.spec.ts | 27 +++ .../src/lib/process/public-process.service.ts | 13 ++ .../case-resource-service.provider.ts | 41 +++- .../petrinet-resource-service.provider.ts | 43 +++- .../process-service.provider.ts | 44 +++- .../task-resource-service.provider.ts | 46 ++++- .../abstract-resource.service.ts | 12 -- .../engine-endpoint/case-resource.service.ts | 7 +- .../petri-net-resource.service.ts | 19 +- .../public-case-resource.service.spec.ts | 27 +++ .../public/public-case-resource.service.ts | 28 +++ .../public-petri-net-resource.service.spec.ts | 27 +++ .../public-petri-net-resource.service.ts | 109 ++++++++++ .../public-task-resource.service.spec.ts | 27 +++ .../public/public-task-resource.service.ts | 193 ++++++++++++++++++ .../engine-endpoint/task-resource.service.ts | 24 +-- .../src/lib/resources/public-api.ts | 5 + 18 files changed, 648 insertions(+), 45 deletions(-) create mode 100644 projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts create mode 100644 projects/netgrif-components-core/src/lib/process/public-process.service.ts create mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts create mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts create mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts create mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts create mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts create mode 100644 projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts diff --git a/projects/netgrif-components-core/src/lib/process/public-api.ts b/projects/netgrif-components-core/src/lib/process/public-api.ts index a79bf4e654..37e8f99ef5 100644 --- a/projects/netgrif-components-core/src/lib/process/public-api.ts +++ b/projects/netgrif-components-core/src/lib/process/public-api.ts @@ -3,6 +3,7 @@ export * from './net'; export * from './transition'; export * from './transaction'; export * from './netRole'; +export * from './public-process.service'; export * from './petri-net-reference-with-permissions'; export * from './permissions'; export * from './rolesAndPermissions' diff --git a/projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts b/projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts new file mode 100644 index 0000000000..cc14c60b4b --- /dev/null +++ b/projects/netgrif-components-core/src/lib/process/public-process.service.spec.ts @@ -0,0 +1,27 @@ +import {TestBed} from '@angular/core/testing'; + +import {PublicProcessService} from './public-process.service'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {ConfigurationService} from '../configuration/configuration.service'; +import {TestConfigurationService} from '../utility/tests/test-config'; + +describe('PublicProcessService', () => { + let service: PublicProcessService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, NoopAnimationsModule], + providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] + }); + service = TestBed.inject(PublicProcessService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/projects/netgrif-components-core/src/lib/process/public-process.service.ts b/projects/netgrif-components-core/src/lib/process/public-process.service.ts new file mode 100644 index 0000000000..f559836f4c --- /dev/null +++ b/projects/netgrif-components-core/src/lib/process/public-process.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import {ProcessService} from './process.service'; +import {PublicPetriNetResourceService} from '../resources/engine-endpoint/public/public-petri-net-resource.service'; +import {LoggerService} from '../logger/services/logger.service'; + +@Injectable({ + providedIn: 'root' +}) +export class PublicProcessService extends ProcessService { + constructor(private _publicPetriNetResource: PublicPetriNetResourceService, private _logger: LoggerService) { + super(_publicPetriNetResource, _logger); + } +} diff --git a/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts index 6e56b015fa..0db74d6de6 100644 --- a/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/case-resource/case-resource-service.provider.ts @@ -1,6 +1,45 @@ + +import { Router } from '@angular/router'; import { CaseResourceService } from '../../resources/engine-endpoint/case-resource.service'; +import { UserService } from '../../user/services/user.service'; +import { SessionService } from '../../authentication/session/services/session.service'; +import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; +import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; +import { ResourceProvider } from '../../resources/resource-provider.service'; +import { ConfigurationService } from '../../configuration/configuration.service'; +import { RedirectService } from '../../routing/redirect-service/redirect.service'; +import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; +import { PublicCaseResourceService } from '../../resources/engine-endpoint/public/public-case-resource.service'; export const CaseResourceServiceProvider = { provide: CaseResourceService, - useClass: CaseResourceService + useFactory: (userService: UserService, + sessionService: SessionService, + authService: AuthenticationService, + router: Router, + publicResolverService: PublicUrlResolverService, + provider: ResourceProvider, + config: ConfigurationService, + redirectService: RedirectService) => { + return publicFactoryResolver( + userService, + sessionService, + authService, + router, + publicResolverService, + new CaseResourceService(provider, config), + new PublicCaseResourceService(provider, config), + redirectService + ); + }, + deps: [ + UserService, + SessionService, + AuthenticationService, + Router, + PublicUrlResolverService, + ResourceProvider, + ConfigurationService, + RedirectService + ] } diff --git a/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts index 69c70d8b18..fae0fbf666 100644 --- a/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/petrinet-resource/petrinet-resource-service.provider.ts @@ -1,6 +1,47 @@ + +import { Router } from '@angular/router'; +import { UserService } from '../../user/services/user.service'; import { PetriNetResourceService } from '../../resources/engine-endpoint/petri-net-resource.service'; +import { SessionService } from '../../authentication/session/services/session.service'; +import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; +import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; +import { ResourceProvider } from '../../resources/resource-provider.service'; +import { ConfigurationService } from '../../configuration/configuration.service'; +import { RedirectService } from '../../routing/redirect-service/redirect.service'; +import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; +import { + PublicPetriNetResourceService +} from '../../resources/engine-endpoint/public/public-petri-net-resource.service'; + export const PetriNetResourceServiceProvider = { provide: PetriNetResourceService, - useClass: PetriNetResourceService + useFactory: (userService: UserService, + sessionService: SessionService, + authService: AuthenticationService, + router: Router, + publicResolverService: PublicUrlResolverService, + provider: ResourceProvider, + config: ConfigurationService, + redirectService: RedirectService) => { + return publicFactoryResolver( + userService, + sessionService, + authService, + router, + publicResolverService, + new PetriNetResourceService(provider, config), + new PublicPetriNetResourceService(provider, config), + redirectService + ); + }, + deps: [ + UserService, + SessionService, + AuthenticationService, + Router, + PublicUrlResolverService, + ResourceProvider, + ConfigurationService, + RedirectService] } diff --git a/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts index 45553ff727..c21d8f4975 100644 --- a/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/process-service/process-service.provider.ts @@ -1,6 +1,48 @@ + +import { Router } from '@angular/router'; import { ProcessService } from '../../process/process.service'; +import { UserService } from '../../user/services/user.service'; +import { SessionService } from '../../authentication/session/services/session.service'; +import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; +import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; +import { PetriNetResourceService } from '../../resources/engine-endpoint/petri-net-resource.service'; +import { PublicPetriNetResourceService } from '../../resources/engine-endpoint/public/public-petri-net-resource.service'; +import { LoggerService } from '../../logger/services/logger.service'; +import { RedirectService } from '../../routing/redirect-service/redirect.service'; +import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; +import { PublicProcessService } from '../../process/public-process.service'; export const ProcessServiceProvider = { provide: ProcessService, - useClass: ProcessService + useFactory: (userService: UserService, + sessionService: SessionService, + authService: AuthenticationService, + router: Router, + publicResolverService: PublicUrlResolverService, + petriNetResource: PetriNetResourceService, + publicPetriNetResource: PublicPetriNetResourceService, + loggerService: LoggerService, + redirectService: RedirectService) => { + return publicFactoryResolver( + userService, + sessionService, + authService, + router, + publicResolverService, + new ProcessService(petriNetResource, loggerService), + new PublicProcessService(publicPetriNetResource, loggerService), + redirectService + ); + }, + deps: [ + UserService, + SessionService, + AuthenticationService, + Router, + PublicUrlResolverService, + PetriNetResourceService, + PublicPetriNetResourceService, + LoggerService, + RedirectService + ] } diff --git a/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts b/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts index d4bfa1de75..dd75860542 100644 --- a/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts +++ b/projects/netgrif-components-core/src/lib/providers/task-resource/task-resource-service.provider.ts @@ -1,6 +1,50 @@ +import { Router } from '@angular/router'; +import { UserService } from '../../user/services/user.service'; import { TaskResourceService } from '../../resources/engine-endpoint/task-resource.service'; +import { SessionService } from '../../authentication/session/services/session.service'; +import { AuthenticationService } from '../../authentication/services/authentication/authentication.service'; +import { PublicUrlResolverService } from '../../public/services/public-url-resolver.service'; +import { LoggerService } from '../../logger/services/logger.service'; +import { ResourceProvider } from '../../resources/resource-provider.service'; +import { ConfigurationService } from '../../configuration/configuration.service'; +import { FieldConverterService } from '../../task-content/services/field-converter.service'; +import { RedirectService } from '../../routing/redirect-service/redirect.service'; +import { publicFactoryResolver } from '../../public/factories/public-factory-resolver'; +import { PublicTaskResourceService } from '../../resources/engine-endpoint/public/public-task-resource.service'; export const TaskResourceServiceProvider = { provide: TaskResourceService, - useClass: TaskResourceService + useFactory: ( + userService: UserService, + sessionService: SessionService, + authService: AuthenticationService, + router: Router, + publicResolverService: PublicUrlResolverService, + logger: LoggerService, + provider: ResourceProvider, + config: ConfigurationService, + fieldConverter: FieldConverterService, + redirectService: RedirectService) => { + return publicFactoryResolver( + userService, + sessionService, + authService, + router, + publicResolverService, + new TaskResourceService(provider, config, fieldConverter, logger), + new PublicTaskResourceService(provider, config, fieldConverter, logger), + redirectService + ); + }, + deps: [ + UserService, + SessionService, + AuthenticationService, + Router, + PublicUrlResolverService, + LoggerService, + ResourceProvider, + ConfigurationService, + FieldConverterService, + RedirectService] } diff --git a/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts index 03270735ce..28627c1308 100644 --- a/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/abstract-endpoint/abstract-resource.service.ts @@ -3,7 +3,6 @@ import {ConfigurationService} from '../../configuration/configuration.service'; import {Page} from '../interface/page'; import {Pagination} from '../interface/pagination'; import {PaginationParams} from '../../utility/pagination/pagination-params'; -import {User} from "../../user/models/user"; /** * The class that contains behavior common to all resource services. @@ -123,15 +122,4 @@ export abstract class AbstractResourceService { } }; } - - protected resolvePublicEndpoint(endpoint: string, user: User): string { - if (!!user && user.isAnonymous()) { - if (endpoint.includes('/')) { - const slashIndex = endpoint.indexOf('/'); - return endpoint.replace(endpoint.substring(slashIndex, slashIndex + 1), '/public/'); - } - return endpoint + '/public'; - } - return endpoint; - } } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts index c65ae941a6..67b4c9763b 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/case-resource.service.ts @@ -15,16 +15,13 @@ import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.se import {EventOutcomeMessageResource} from '../interface/message-resource'; import {CreateCaseRequestBody} from '../interface/create-case-request-body'; import {HttpParams} from "@angular/common/http"; -import {UserService} from "../../user/services/user.service"; @Injectable({ providedIn: 'root' }) export class CaseResourceService extends AbstractResourceService implements CountService { - constructor(provider: ResourceProvider, - configService: ConfigurationService, - protected userService: UserService) { + constructor(provider: ResourceProvider, configService: ConfigurationService) { super('case', provider, configService); } @@ -98,7 +95,7 @@ export class CaseResourceService extends AbstractResourceService implements Coun * {{baseUrl}}/api/workflow/case */ public createCase(body: CreateCaseRequestBody): Observable { - return this._resourceProvider.post$(this.resolvePublicEndpoint('workflow/case/', this.userService.user), this.SERVER_URL, body).pipe(map(r => this.changeType(r, undefined))); + return this._resourceProvider.post$('workflow/case/', this.SERVER_URL, body).pipe(map(r => this.changeType(r, undefined))); } /** diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts index 06c28a93da..868ba25350 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/petri-net-resource.service.ts @@ -14,16 +14,13 @@ import {Page} from '../interface/page'; import {processMessageResponse} from '../../utility/process-message-response'; import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.service'; import RolesAndPermissions from '../../process/rolesAndPermissions'; -import {UserService} from "../../user/services/user.service"; @Injectable({ providedIn: 'root' }) export class PetriNetResourceService extends AbstractResourceService { - constructor(provider: ResourceProvider, - configService: ConfigurationService, - protected userService: UserService,) { + constructor(provider: ResourceProvider, configService: ConfigurationService) { super('petrinet', provider, configService); } @@ -48,7 +45,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/data */ public getDataPetriNet(body: object): Observable { // TODO: response - return this._resourceProvider.post$(this.resolvePublicEndpoint('petrinet', this.userService.user) + '/data', this.SERVER_URL, body) + return this._resourceProvider.post$('petrinet/data', this.SERVER_URL, body) .pipe(map(r => this.changeType(r, undefined))); } @@ -60,7 +57,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/transitions */ public getPetriNetTransitions(netId: string): Observable> { - return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/transitions', this.userService.user), this.SERVER_URL, new HttpParams().set('ids', netId)) + return this._resourceProvider.get$('/petrinet/transitions', this.SERVER_URL, new HttpParams().set('ids', netId)) .pipe(map(r => this.changeType(r, 'transitionReferences'))); } @@ -72,7 +69,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id}/transactions */ public getPetriNetTransactions(netId: string, params?: Params): Observable> { - return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + netId + '/transactions', this.userService.user), this.SERVER_URL, params) + return this._resourceProvider.get$('/petrinet/' + netId + '/transactions', this.SERVER_URL, params) .pipe(map(r => this.changeType(r, 'transactions'))); } @@ -84,7 +81,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id}/roles */ public getPetriNetRoles(netId: string, params?: Params): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + netId + '/roles', this.userService.user), this.SERVER_URL, params) + return this._resourceProvider.get$('/petrinet/' + netId + '/roles', this.SERVER_URL, params) .pipe(map(r => this.changeType(r, undefined))); } @@ -119,7 +116,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{identifier}/{version} */ public getOne(identifier: string, version: string, params?: Params): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + btoa(identifier) + '/' + version, this.userService.user), this.SERVER_URL, params) + return this._resourceProvider.get$('petrinet/' + btoa(identifier) + '/' + version, this.SERVER_URL, params) .pipe(map(r => this.changeType(r, 'petriNetReferences'))); } @@ -131,7 +128,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/{id} */ public getOneById(netId: string, params?: Params): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint('petrinet/' + netId, this.userService.user), this.SERVER_URL, params) + return this._resourceProvider.get$('petrinet/' + netId, this.SERVER_URL, params) .pipe(map(r => this.changeType(r, undefined))); } @@ -168,7 +165,7 @@ export class PetriNetResourceService extends AbstractResourceService { * **Request URL:** {{baseUrl}}/api/petrinet/search */ public searchPetriNets(body: PetriNetRequestBody, params?: Params): Observable> { - return this._resourceProvider.post$(this.resolvePublicEndpoint('petrinet/search', this.userService.user), this.SERVER_URL, body, params) + return this._resourceProvider.post$('petrinet/search', this.SERVER_URL, body, params) // .pipe(map(r => this.getResourcePage(r, 'petriNetReferences'))); .pipe(map(r => this.mapToPage(r))); } diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts new file mode 100644 index 0000000000..89281b968a --- /dev/null +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.spec.ts @@ -0,0 +1,27 @@ +import {TestBed} from '@angular/core/testing'; + +import {PublicCaseResourceService} from './public-case-resource.service'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {TestConfigurationService} from '../../../utility/tests/test-config'; + +describe('PublicCaseResourceService', () => { + let service: PublicCaseResourceService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, NoopAnimationsModule], + providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] + }); + service = TestBed.inject(PublicCaseResourceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + afterEach( () => { + TestBed.resetTestingModule(); + }); +}); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts new file mode 100644 index 0000000000..07dad17ee5 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-case-resource.service.ts @@ -0,0 +1,28 @@ +import {Injectable} from '@angular/core'; + +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {CaseResourceService} from '../case-resource.service'; +import {ResourceProvider} from '../../resource-provider.service'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {EventOutcomeMessageResource} from '../../interface/message-resource'; + +@Injectable({ + providedIn: 'root' +}) +export class PublicCaseResourceService extends CaseResourceService { + + constructor(provider: ResourceProvider, configService: ConfigurationService) { + super(provider, configService); + } + + /** + * Create new case + * POST + * {{baseUrl}}/api/workflow/case + */ + public createCase(body: object): Observable { + return this._resourceProvider.post$('workflow/public/case/', this.SERVER_URL, body) + .pipe(map(r => this.changeType(r, undefined))); + } +} diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts new file mode 100644 index 0000000000..be67439287 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.spec.ts @@ -0,0 +1,27 @@ +import {TestBed} from '@angular/core/testing'; + +import {PublicPetriNetResourceService} from './public-petri-net-resource.service'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {TestConfigurationService} from '../../../utility/tests/test-config'; + +describe('PublicPetriNetResourceService', () => { + let service: PublicPetriNetResourceService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, NoopAnimationsModule], + providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] + }); + service = TestBed.inject(PublicPetriNetResourceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts new file mode 100644 index 0000000000..e2a07efe69 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-petri-net-resource.service.ts @@ -0,0 +1,109 @@ +import { Injectable } from '@angular/core'; +import {PetriNetResourceService} from '../petri-net-resource.service'; +import {Params, ResourceProvider} from '../../resource-provider.service'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {Observable} from 'rxjs'; +import {PetriNet} from '../../interface/petri-net'; +import {PetriNetReference} from '../../interface/petri-net-reference'; +import {map} from 'rxjs/operators'; +import {PetriNetRequestBody} from '../../interface/petri-net-request-body'; +import {Page} from '../../interface/page'; +import Transaction from '../../../process/transaction'; +import Transition from '../../../process/transition'; +import {HttpParams} from '@angular/common/http'; +import RolesAndPermissions from '../../../process/rolesAndPermissions'; + +@Injectable({ + providedIn: 'root' +}) +export class PublicPetriNetResourceService extends PetriNetResourceService { + + constructor(protected provider: ResourceProvider, protected _configService: ConfigurationService) { + super(provider, _configService); + } + + /** + * get One Net by ID + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/petrinet/public/{id} + */ + public getOneById(netId: string, params?: Params): Observable { + return this.provider.get$('petrinet/public/' + netId, this.SERVER_URL, params) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * get One Net + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/petrinet/public/{identifier}/{version} + */ + public getOne(identifier: string, version: string, params?: Params): Observable { + return this.provider.get$('petrinet/public/' + btoa(identifier) + '/' + version, this.SERVER_URL, params) + .pipe(map(r => this.changeType(r, 'petriNetReferences'))); + } + + /** + * search PetriNets + * + * **Request Type:** POST + * + * **Request URL:** {{baseUrl}}/api/petrinet/search + */ + public searchPetriNets(body: PetriNetRequestBody, params?: Params): Observable> { + return this._resourceProvider.post$('petrinet/public/search', this.SERVER_URL, body, params) + // .pipe(map(r => this.getResourcePage(r, 'petriNetReferences'))); + .pipe(map(r => this.mapToPage(r))); + } + + /** + * Get Roles References Using + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/petrinet/{id}/roles + */ + public getPetriNetRoles(netId: string, params?: Params): Observable { + return this._resourceProvider.get$('petrinet/public/' + netId + '/roles', this.SERVER_URL, params) + .pipe(map(r => this.changeType(r, 'processRoles'))); + } + + /** + * Get Transaction References Using + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/petrinet/{id}/transactions + */ + public getPetriNetTransactions(netId: string, params?: Params): Observable> { + return this._resourceProvider.get$('petrinet/public/' + netId + '/transactions', this.SERVER_URL, params) + .pipe(map(r => this.changeType(r, 'transactions'))); + } + + /** + * Get Data Field References Using + * + * **Request Type:** POST + * + * **Request URL:** {{baseUrl}}/api/petrinet/data + */ + public getDataPetriNet(body: object): Observable { // TODO: response + return this._resourceProvider.post$('petrinet/public/data', this.SERVER_URL, body) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Get Transition References Using + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/petrinet/transitions + */ + public getPetriNetTransitions(netId: string): Observable> { + return this._resourceProvider.get$('petrinet/public/transitions', this.SERVER_URL, new HttpParams().set('ids', netId)) + .pipe(map(r => this.changeType(r, 'transitionReferences'))); + } +} diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts new file mode 100644 index 0000000000..c044130840 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.spec.ts @@ -0,0 +1,27 @@ +import {TestBed} from '@angular/core/testing'; + +import {PublicTaskResourceService} from './public-task-resource.service'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {TestConfigurationService} from '../../../utility/tests/test-config'; + +describe('PublicTaskResourceService', () => { + let service: PublicTaskResourceService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, NoopAnimationsModule], + providers: [{provide: ConfigurationService, useClass: TestConfigurationService}] + }); + service = TestBed.inject(PublicTaskResourceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts new file mode 100644 index 0000000000..203a6d2fe9 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts @@ -0,0 +1,193 @@ +import {Injectable} from '@angular/core'; +import {TaskResourceService} from '../task-resource.service'; +import {FieldConverterService} from '../../../task-content/services/field-converter.service'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {Params, ProviderProgress, ResourceProvider} from '../../resource-provider.service'; +import {LoggerService} from '../../../logger/services/logger.service'; +import {Observable} from 'rxjs'; +import {filter, map} from 'rxjs/operators'; +import {FilterType} from '../../../filter/models/filter-type'; +import {Filter} from '../../../filter/models/filter'; +import {Page} from '../../interface/page'; +import {TaskSetDataRequestBody} from '../../interface/task-set-data-request-body'; +import {TaskReference} from '../../interface/task-reference'; +import {Task} from '../../interface/task'; +import {HttpEventType, HttpParams} from '@angular/common/http'; +import {EventOutcomeMessageResource, MessageResource} from '../../interface/message-resource'; +import {FileFieldRequest} from "../../interface/file-field-request-body"; + +@Injectable({ + providedIn: 'root' +}) +export class PublicTaskResourceService extends TaskResourceService { + + constructor(protected _provider: ResourceProvider, + protected _configService: ConfigurationService, + protected _fieldConverter: FieldConverterService, + protected _logger: LoggerService) { + super(_provider, _configService, _fieldConverter, _logger); + } + + /** + * Assign task + * GET + */ + // {{baseUrl}}/api/task/public/assign/:id + public assignTask(taskId: string): Observable { + return this._provider.get$('task/public/assign/' + taskId, this.SERVER_URL) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Cancel task + * GET + */ + // {{baseUrl}}/api/task/public/cancel/:id + public cancelTask(taskId: string): Observable { + return this._provider.get$('task/public/cancel/' + taskId, this.SERVER_URL) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Finish task + * GET + */ + // {{baseUrl}}/api/task/public/finish/:id + public finishTask(taskId: string): Observable { + return this._provider.get$('task/public/finish/' + taskId, this.SERVER_URL) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Get tasks of the case + * GET + */ + // {{baseUrl}}/api/task/public/case/:id + public getAllTasksByCase(caseId: string): Observable> { + return this._provider.get$('task/public/case/' + caseId, this.SERVER_URL) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Get all task data + * + * GET + * + * If you don't want to parse the response yourself use [getData]{@link TaskResourceService#getData} instead. + * + * @returns the raw backend response without any additional processing + */ + // {{baseUrl}}/api/task/public/:id/data + public rawGetData(taskId: string): Observable { + return this._provider.get$('task/public/' + taskId + '/data', this.SERVER_URL) + .pipe(map(r => this.changeType(r, 'dataGroups'))); + } + + /** + * Set task data + * POST + */ + // {{baseUrl}}/api/task/public/:id/data + public setData(taskId: string, body: TaskSetDataRequestBody): Observable { + return this._provider.post$('task/public/' + taskId + '/data', this.SERVER_URL, body) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Searches tasks trough the Mongo endpoint. + * POST + * @param filterParam filter used to search the tasks. Must be of type `TASK`. + * Note that the `query` attribute of the filter cannot be used with this endpoint. + * Attempting to use it will display a warning and remove the attribute from the request. + * @param params Additional request parameters + */ + // {{baseUrl}}/api/task/public/search + public getTasks(filterParam: Filter, params?: Params): Observable> { + if (filterParam.type !== FilterType.TASK) { + throw new Error('Provided filter doesn\'t have type TASK'); + } + + if (filterParam.bodyContainsQuery()) { + throw new Error('getTasks endpoint cannot be queried with filters that contain the \'query\' attribute'); + } + + params = ResourceProvider.combineParams(filterParam.getRequestParams(), params); + return this._provider.post$('task/public/search', this.SERVER_URL, filterParam.getRequestBody(), params) + .pipe(map(r => this.getResourcePage(r, 'tasks'))); + } + + /** + * Download task file field value + * GET + */ + public downloadFile(taskId: string, params: HttpParams): Observable { + const url = `task/public/${taskId}/file${params?.has("fileName") ? '/named' : ''}`; + return this._resourceProvider.getBlob$(url, this.SERVER_URL, params).pipe( + map(event => { + switch (event.type) { + case HttpEventType.DownloadProgress: + return ResourceProvider.getProgress(event); + case HttpEventType.Response: + return event.body; + default: + return undefined; + } + }), + filter(value => !!value) + ); + } + + /** + * Upload file into the task + * POST + */ + public uploadFile(taskId: string, body: object, multipleFiles: boolean): + Observable { + const url = `task/public/${taskId}/${multipleFiles ? 'files' : 'file'}`; + return this._resourceProvider.postWithEvent$(url, this.SERVER_URL, body).pipe( + map(event => { + switch (event.type) { + case HttpEventType.UploadProgress: + return ResourceProvider.getProgress(event); + case HttpEventType.Response: + return event.body; + default: + return undefined; + } + }), + filter(value => !!value) + ); + } + + /** + * Delete file from the task + * DELETE + */ + public deleteFile(taskId: string, body: FileFieldRequest): Observable { + const url = `task/public/${taskId}/file${body.fileName ? '/named' : ''}`; + return this._resourceProvider.delete$(url, this.SERVER_URL, {}, {}, 'json', body).pipe( + map(r => this.changeType(r, undefined)) + ); + } + + /** + * Download task file preview for field value + * GET + */ + public downloadFilePreview(taskId: string, params: HttpParams): Observable { + const url = `task/public/${taskId}/file_preview`; + return this._resourceProvider.getBlob$(url, this.SERVER_URL, params).pipe( + map(event => { + switch (event.type) { + case HttpEventType.DownloadProgress: + return ResourceProvider.getProgress(event); + case HttpEventType.Response: + return event.body; + default: + return undefined; + } + }), + filter(value => !!value) + ); + } +} diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts index 96645c6f78..0ca953b97f 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/task-resource.service.ts @@ -20,7 +20,6 @@ import {DataGroup} from '../interface/data-groups'; import {DataField} from '../../data-fields/models/abstract-data-field'; import {GetDataGroupsEventOutcome} from '../../event/model/event-outcomes/data-outcomes/get-data-groups-event-outcome'; import {FileFieldRequest} from "../interface/file-field-request-body"; -import {UserService} from "../../user/services/user.service"; @Injectable({ providedIn: 'root' @@ -30,8 +29,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun constructor(provider: ResourceProvider, configService: ConfigurationService, protected _fieldConverter: FieldConverterService, - protected _logger: LoggerService, - protected userService: UserService) { + protected _logger: LoggerService) { super('task', provider, configService); } @@ -64,7 +62,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/assign/:id public assignTask(taskId: string): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint('task/assign/' + taskId, this.userService.user), this.SERVER_URL) + return this._resourceProvider.get$('task/assign/' + taskId, this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -74,7 +72,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/cancel/:id public cancelTask(taskId: string): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint( 'task/cancel/' + taskId, this.userService.user), this.SERVER_URL) + return this._resourceProvider.get$('task/cancel/' + taskId, this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -94,7 +92,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/finish/:id public finishTask(taskId: string): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint('task/finish/' + taskId, this.userService.user), this.SERVER_URL) + return this._resourceProvider.get$('task/finish/' + taskId, this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -144,7 +142,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/case public getAllTasksByCases(caseIds: string[]): Observable> { - return this._resourceProvider.post$(this.resolvePublicEndpoint('task/case', this.userService.user), this.SERVER_URL, caseIds) + return this._resourceProvider.post$('task/case', this.SERVER_URL, caseIds) .pipe(map(r => this.getResourcePage(r, 'tasks'))); } @@ -154,7 +152,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/case/:id public getAllTasksByCase(caseId: string): Observable> { - return this._resourceProvider.get$(this.resolvePublicEndpoint('task/case/' + caseId, this.userService.user), this.SERVER_URL) + return this._resourceProvider.get$('task/case/' + caseId, this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -190,7 +188,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/:id/data public rawGetData(taskId: string): Observable { - return this._resourceProvider.get$(this.resolvePublicEndpoint('task/' + taskId + '/data', this.userService.user), this.SERVER_URL) + return this._resourceProvider.get$('task/' + taskId + '/data', this.SERVER_URL) .pipe(map(r => this.changeType(r, undefined))); } @@ -256,7 +254,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun */ // {{baseUrl}}/api/task/:id/data public setData(taskId: string, body: TaskSetDataRequestBody): Observable { - return this._resourceProvider.post$(this.resolvePublicEndpoint('task/' + taskId + '/data', this.userService.user), this.SERVER_URL, body) + return this._resourceProvider.post$('task/' + taskId + '/data', this.SERVER_URL, body) .pipe(map(r => this.changeType(r, undefined))); } @@ -268,7 +266,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun // {{baseUrl}}/api/task/:id/file/:field - for file field // {{baseUrl}}/api/task/:id/file/:field/:name - for file list field public downloadFile(taskId: string, params: HttpParams): Observable { - const url = this.resolvePublicEndpoint(`task/${taskId}/file${params?.has("fileName") ? '/named' : ''}`, this.userService.user); + const url = `task/${taskId}/file${params?.has("fileName") ? '/named' : ''}`; return this._resourceProvider.getBlob$(url, this.SERVER_URL, params).pipe( map(event => { switch (event.type) { @@ -292,7 +290,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun // {{baseUrl}}/api/task/:id/files/:field - for file list field public uploadFile(taskId: string, body: object, multipleFiles: boolean): Observable { - const url = this.resolvePublicEndpoint(`task/${taskId}/${multipleFiles ? 'files' : 'file'}`, this.userService.user); + const url = `task/${taskId}/${multipleFiles ? 'files' : 'file'}`; return this._resourceProvider.postWithEvent$(url, this.SERVER_URL, body).pipe( map(event => { switch (event.type) { @@ -313,7 +311,7 @@ export class TaskResourceService extends AbstractResourceService implements Coun * DELETE */ public deleteFile(taskId: string, body?: FileFieldRequest): Observable { - const url = this.resolvePublicEndpoint(`task/${taskId}/file${body?.fileName ? '/named' : ''}`, this.userService.user); + const url = `task/${taskId}/file${body?.fileName ? '/named' : ''}`; return this._resourceProvider.delete$(url , this.SERVER_URL, {}, {}, 'json', body).pipe( map(r => this.changeType(r, undefined)) ); diff --git a/projects/netgrif-components-core/src/lib/resources/public-api.ts b/projects/netgrif-components-core/src/lib/resources/public-api.ts index 8a39c44212..6ee3de7108 100644 --- a/projects/netgrif-components-core/src/lib/resources/public-api.ts +++ b/projects/netgrif-components-core/src/lib/resources/public-api.ts @@ -8,6 +8,11 @@ export * from './engine-endpoint/dashboard-resource.service'; export * from './engine-endpoint/ldap-group-resource.service'; export * from './engine-endpoint/configuration-resource.service'; +/* PUBLIC SERVICES */ +export * from './engine-endpoint/public/public-case-resource.service'; +export * from './engine-endpoint/public/public-petri-net-resource.service'; +export * from './engine-endpoint/public/public-task-resource.service'; + export * from './interface/author'; export * from './interface/immediate-data'; export * from './interface/response-data'; From 0305882d83826a208c37f2c04f94aa2be08f65bf Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 4 Feb 2026 10:03:46 +0100 Subject: [PATCH 07/16] [NAE-2241] Anonymous access refactor Implemented a new method in PublicTaskResource to retrieve tasks using case IDs. Refactored authentication logic by replacing anonymous user checks with `isAnonymous()` and removed unused AnonymousService. Updated API endpoint configurations to use a consistent base URL and improved code clarity. --- nae.json | 20 +++++++++---------- .../src/lib/authentication/public-api.ts | 2 -- .../factories/public-factory-resolver.ts | 2 +- .../public/public-task-resource.service.ts | 9 +++++++++ .../service/task-view.service.spec.ts | 6 +++++- .../task-view/service/task-view.service.ts | 2 +- 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/nae.json b/nae.json index 02c08c4b8e..f919d17fdd 100644 --- a/nae.json +++ b/nae.json @@ -3,8 +3,8 @@ "extends": "nae-default", "providers": { "auth": { - "address": "http://localhost:8800/api/", - "authentication": "BasicWithRealm", + "address": "http://localhost:8080/api/", + "authentication": "Basic", "endpoints": { "login": "auth/login", "logout": "auth/logout", @@ -24,44 +24,44 @@ "enable": false, "clientId": "dev-cluster-worker", "redirectUrl": "http://localhost:8081/realms/netgrif-cloud-testing/protocol/openid-connect/auth", - "refreshUrl": "http://localhost:8800/api/auth/login", + "refreshUrl": "http://localhost:8080/api/auth/login", "scopes": ["openid","email","profile","roles"] } }, "resources": [ { "name": "case", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" }, { "name": "task", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" }, { "name": "petrinet", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" }, { "name": "user", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" }, { "name": "dashboard", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" }, { "name": "filter", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" }, { "name": "impersonation", - "address": "http://localhost:8800/api/", + "address": "http://localhost:8080/api/", "format": "json" } ] diff --git a/projects/netgrif-components-core/src/lib/authentication/public-api.ts b/projects/netgrif-components-core/src/lib/authentication/public-api.ts index b7b1c6a414..f5ff8e8865 100644 --- a/projects/netgrif-components-core/src/lib/authentication/public-api.ts +++ b/projects/netgrif-components-core/src/lib/authentication/public-api.ts @@ -8,8 +8,6 @@ export * from './sign-up/public-api'; /* MODULES */ export * from './authentication.module'; -/* SERVICES */ -export * from './anonymous/anonymous.service'; // export * from './services/anonymous-authentication-interceptor' export * from './services/authentication-interceptor' export * from './proxyAuthentication.service' diff --git a/projects/netgrif-components-core/src/lib/public/factories/public-factory-resolver.ts b/projects/netgrif-components-core/src/lib/public/factories/public-factory-resolver.ts index f343dd8e0d..a2790c8b9d 100644 --- a/projects/netgrif-components-core/src/lib/public/factories/public-factory-resolver.ts +++ b/projects/netgrif-components-core/src/lib/public/factories/public-factory-resolver.ts @@ -15,7 +15,7 @@ export const publicFactoryResolver = (userService: UserService, sessionService: } else { router.navigate([url], {queryParams: redirectService.queryParams}); } - } else if (authService.isAuthenticated && userService.user.id !== '' && userService.user.email !== 'anonymous@netgrif.com') { + } else if (authService.isAuthenticated && !!userService.user && !userService.user.isAnonymous()) { return privateService; } else { return publicService; diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts index 203a6d2fe9..6ba4e37d60 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/public/public-task-resource.service.ts @@ -58,6 +58,15 @@ export class PublicTaskResourceService extends TaskResourceService { .pipe(map(r => this.changeType(r, undefined))); } + /** + * Get tasks of the case + * GET + */ + public getAllTasksByCases(caseIds: string[]): Observable> { + return this._resourceProvider.post$('task/public/case', this.SERVER_URL, caseIds) + .pipe(map(r => this.getResourcePage(r, 'tasks'))); + } + /** * Get tasks of the case * GET diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.spec.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.spec.ts index 89198b8369..83732a92aa 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.spec.ts @@ -89,7 +89,7 @@ describe('TaskViewService', () => { // NAE-968 it('should process second filter change before first filter call returns', fakeAsync(() => { let tasks: Array; - + console.log(service); service.tasks$.subscribe(receivedTasks => { tasks = receivedTasks; }); @@ -173,4 +173,8 @@ class MyResources { getTasks(): Observable> { return this.returnResponse(); } + + public getAllTasksByCases(caseIds: string[]): Observable> { + return this.returnResponse(); + } } diff --git a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts index 2598bf0571..58668ff24e 100644 --- a/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts +++ b/projects/netgrif-components-core/src/lib/view/task-view/service/task-view.service.ts @@ -223,7 +223,7 @@ export class TaskViewService extends AbstractSortableViewComponent implements On let request: Observable>; if (this._userService.user.isAnonymous()) { - const caseIds = Array.isArray(requestContext.filter.getRequestBody()['case']) ? requestContext.filter.getRequestBody()['case'][0].id : [requestContext.filter.getRequestBody()['case'].id]; + const caseIds = Array.isArray(requestContext.filter.getRequestBody()['case']) ? requestContext.filter.getRequestBody()['case'][0].id : [requestContext.filter.getRequestBody()['case']?.id ?? '']; request = this._taskService.getAllTasksByCases(caseIds).pipe(take(1)); } else if (requestContext.filter.bodyContainsQuery() || this._preferredEndpoint === TaskEndpoint.ELASTIC) { request = timer(200).pipe( From 9affb70aeee01258fe91e74e4bbaffaac1e3cc01 Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Thu, 18 Jun 2026 01:40:29 +0200 Subject: [PATCH 08/16] Release 7.0.0 --- CHANGELOG.md | 89 ++++++++++++++++++- package.json | 2 +- projects/netgrif-components-core/package.json | 2 +- projects/netgrif-components/package.json | 4 +- 4 files changed, 92 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62498385cf..4d83fdad5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,94 @@ Full Changelog: [https://github.com/netgrif/components/commits/v6.5.0](https://g - [NAE-2019] Bugs after merge with 6.4.0 ### Changed -- [NAE- 1940] Update to new Angular +- [NAE-1889] Process list is not refreshed after uploading process file +- [NAE-1885] Side panel creates a new case when pressing Enter on date field +- [NAE-1687] Factory class as providers +- [NAE-1911] Autosave on text area in cooperation with button +- [NAE-1497] Frontend actions +- [NAE-1915] TaskRef behaviour handling for multiple level +- [NAE-1904] Case ref as multichoice and enumeration +- [NAE-1908] NAE-1906 Improvements +- [NAE-1918] Tags on process resources +- [NAE-1921] User field value cannot be cleared +- [NAE-1914] revertToPreviousValue - change is always false +- [NAE-1925] Panel is not opening +- [NAE-1924] Neziadane volanie setData z FE +- [NAE-1923] DateTime doesn't have locale and rework validation to isoWeekday +- [NAE-1928] Refresh tabs on change +- [NAE-1926] Can't close Tab in Tab view +- [NAE-1929] Data field type list of strings +- [NAE-1933] UserList deleteAll button +- [NAE-1939] Problem with Tests +- [NAE-1936] Disable create case button using menu items +- [NAE-1935] Improved breadcrumbs from menu items +- [NAE-1876] Process URI v2 +- [NAE-1882] Filter folder process +- [NAE-1890] Data field component register +- [NAE-1901] Taskref rendering update +- [NAE-1900] New component design +- [NAE-1920] Injection token NAE_USER_ASSIGN_COMPONENT breaks delegate +- [NAE-1879] Language register +- [NAE-1905] Add bold on i18n text plainText field +- [NAE-1873] Seperator for number field +- [NAE-1922] Signature Pad Field +- [NAE-1949] Allowed Types for Filefield +- [NAE-1813] Field id as path variable in TaskController +- [NAE-1957] Allow filter to caseRef field and variants +- [NAE-1960] Enumeration Map does not propagate changes when selecting +- [NAE-1958] Make component properties changeable +- [NAE- 1940] Update to new Angular +- [NAE-1983] Public view file handling +- [NAE-1999] Broken pagination on paged case view +- [NAE-1949] Allowed Types for Filefield +- [NAE-2005] Field behavior change does not work correctly with multiple references using taskRef +- [NAE-2013] Autocomplete options are set to the first dropdown +- [NAE-2013] Autocomplete options are set to the first dropdown +- [NAE-2016] Global roles for menu items permissions +- [NAE-2018] User list input is not showing dialog +- [NAE-2020] Create case error when allowed net blocks are present +- [NAE-2021] Outputs for navigation components +- [NAE-2022] UI Design Fixes and Improvements +- [NAE-2034] Open first view +- [NAE-2038] Public View +- [NAE-2033] Welcome dashboard +- [NAE-2035] Implement Single-Task-View +- [NAE-2036] Task-List-View to open Case by link +- [NAE-2041] Implementing Ticket View +- [NAE-2040] Search in role management +- [NAE-2052] Integrate ticket view with menu items +- [NAE-2039] Search in workflow view +- [NAE-2063] Action API 6.5.0 +- [NAE-2115] Task search on Search Node #314 +- [NAE-2119] Fix menuItem +- [NAE-2125] Remove URI service usage from admin and menu items #318 +- [NAE-2116] Frontend remote configuration +- [NAE-2085] Refactor User +- [NAE-2122] Implement Structured and Efficient Pagination in gRPC +- [NAE-2146] Broken hidden menu on frontend +- [NAE-2085] Refactor User +- [NAE-2118] Implement OpenID Connector Auth for Admin node +- [NAE-2165] Broken task list pagination +- [NAE-2174] Vanishing menu after few clicks +- Refactor case ID usage to replace `stringId` with `id` +- [NAE-2188] Wrong remote configuration loading order +- [NAE-2197] Wrong elastic sort +- [NAE-2205] DefaultCaseRefListViewComponent - headers not displayed when processes exceed single view +- [NAE-2202] Post test fixes +- [NAE-2218] Misaligned header on case view when displayed below 1920px +- [NAE-2224] Title in optional text in case creation button as I18nString +- [NAE-2226] Cannot switch between menu items +- [NAE-2232] Frontend Actions Task events resolver +- [NAE-2233] Fix dashboard menu issue +- [NAE-2227] MenuItem default headers not working +- [NAE-2234] Fix dashboard menu +- [NAE-2217] Single Task View +- [NAE-2251] Map field options are not translated +- [NAE-2285] Group as a value of userlists +- [NAE-2354] Include assignee userRealmId in task response +- [NAE-2263] Copy/selection prevention is set to to entire application not just for side menu +- [NAE-2435] Enumeration field with no choices does not properly handle validation +- [NAE-2416] AbstractFileDefaultFieldComponent does not push upload event ## [6.5.0](https://github.com/netgrif/components/releases/tag/v6.5.0) (2025-02-18) ### Added diff --git a/package.json b/package.json index f2bed4cbfc..b758cf5060 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/components-project", - "version": "7.0.0-rc.19", + "version": "7.0.0", "description": "Netgrif Application Engine Frontend project. Project includes angular libraries as base for NAE applications.", "homepage": "https://components.netgrif.com", "license": "SEE LICENSE IN LICENSE", diff --git a/projects/netgrif-components-core/package.json b/projects/netgrif-components-core/package.json index bba6174408..e2e241993c 100644 --- a/projects/netgrif-components-core/package.json +++ b/projects/netgrif-components-core/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/components-core", - "version": "7.0.0-rc.19", + "version": "7.0.0", "description": "Netgrif Application engine frontend core Angular library", "homepage": "https://components.netgrif.com", "license": "SEE LICENSE IN LICENSE", diff --git a/projects/netgrif-components/package.json b/projects/netgrif-components/package.json index 2c1be258e1..9cb7c2985f 100644 --- a/projects/netgrif-components/package.json +++ b/projects/netgrif-components/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/components", - "version": "7.0.0-rc.19", + "version": "7.0.0", "description": "Netgrif Application Engine frontend Angular components", "homepage": "https://components.netgrif.com", "license": "SEE LICENSE IN LICENSE", @@ -29,7 +29,7 @@ "nae frontend" ], "peerDependencies": { - "@netgrif/components-core": "7.0.0-rc.19", + "@netgrif/components-core": "7.0.0", "@angular-material-components/datetime-picker": "~16.0.0", "@angular-material-components/moment-adapter": "~16.0.0", "@angular/animations": "~17.1.0", From 38d6f664c93c567f8c7e294818ad7c362ee3fda0 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 6 Jul 2026 15:33:54 +0200 Subject: [PATCH 09/16] [NAE-2464] Release 1.0.1 Bugfixes Updated `elasticKeywords` logic to include the `Equals` operator alongside `Substring` in the identifier search. This ensures better matching capabilities for both `case-string-id` and `case-visual-id` categories. --- .../src/lib/search/models/category/case/case-string-id.ts | 2 +- .../src/lib/search/models/category/case/case-visual-id.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/search/models/category/case/case-string-id.ts b/projects/netgrif-components-core/src/lib/search/models/category/case/case-string-id.ts index 7b1f6ef09b..895ec157e6 100644 --- a/projects/netgrif-components-core/src/lib/search/models/category/case/case-string-id.ts +++ b/projects/netgrif-components-core/src/lib/search/models/category/case/case-string-id.ts @@ -41,7 +41,7 @@ export class CaseStringId extends NoConfigurationCategory { protected get elasticKeywords(): Array { if (!!this._optionalDependencies) { const resolver = this._optionalDependencies.searchIndexResolver; - return [resolver.getCoreIndex(CaseSearch.STRING_ID, this.isSelectedOperator(Substring))]; + return [resolver.getCoreIndex(CaseSearch.STRING_ID, this.isSelectedOperator(Substring) || this.isSelectedOperator(Equals))]; } else { return this._elasticKeywords; } diff --git a/projects/netgrif-components-core/src/lib/search/models/category/case/case-visual-id.ts b/projects/netgrif-components-core/src/lib/search/models/category/case/case-visual-id.ts index d009b9fed9..09efdca346 100644 --- a/projects/netgrif-components-core/src/lib/search/models/category/case/case-visual-id.ts +++ b/projects/netgrif-components-core/src/lib/search/models/category/case/case-visual-id.ts @@ -37,7 +37,7 @@ export class CaseVisualId extends NoConfigurationCategory { protected get elasticKeywords(): Array { if (!!this._optionalDependencies) { const resolver = this._optionalDependencies.searchIndexResolver; - return [resolver.getCoreIndex(CaseSearch.VISUAL_ID, this.isSelectedOperator(Substring))]; + return [resolver.getCoreIndex(CaseSearch.VISUAL_ID, this.isSelectedOperator(Substring) || this.isSelectedOperator(Equals))]; } else { return this._elasticKeywords; } From 09b6ec6f6b0103f67c2acbd4182373ba6e0d0438 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 4 Aug 2026 10:42:39 +0200 Subject: [PATCH 10/16] Refactor tab switching to handle concurrent updates safely Introduce a mechanism to prevent race conditions during tab switching by adding a `_switching` flag and `_pendingIndex`. This ensures seamless handling of concurrent user actions and avoids inconsistencies in the selected tab state. --- .../src/lib/tabs/classes/tab-view.ts | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts b/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts index bc2feec17e..96612fc4f3 100644 --- a/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts +++ b/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts @@ -33,6 +33,11 @@ export class TabView implements TabViewInterface { public selectedIndex: FormControl; private uniqueIdCounter = new IncrementingCounter(); + + private _switching = false; + + private _pendingIndex: number; + /** * @ignore * Holds a reference to an object that hides some public attributes and methods from tabs. @@ -274,17 +279,35 @@ export class TabView implements TabViewInterface { } public tabChange(event: MatTabChangeEvent) { - if (event.index !== this.selectedIndex.value) { + this._pendingIndex = event.index; + if (this._switching) { + return; // an update is already being processed; the pending value will be picked up after + } + this._processSwitch(); + } + + private _processSwitch() { + this._switching = true; + const index = this._pendingIndex!; + + if (index !== this.selectedIndex.value) { let tab = this.openedTabs[this.selectedIndex.value]; if (tab) { tab.tabSelected$.next(false); } - tab = this.openedTabs[event.index]; + tab = this.openedTabs[index]; if (tab) { tab.tabSelected$.next(true); } - this.selectedIndex.setValue(event.index); + this.selectedIndex.setValue(index); } + + setTimeout(() => { + this._switching = false; + if (this._pendingIndex !== index) { + this._processSwitch(); + } + }, 150); } /** From afd848110b85d36484141ea592d59adbf6f1b1d5 Mon Sep 17 00:00:00 2001 From: Machac Date: Tue, 4 Aug 2026 12:55:58 +0200 Subject: [PATCH 11/16] Release 7.0.2 --- package.json | 2 +- projects/netgrif-components-core/package.json | 2 +- projects/netgrif-components/package.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b758cf5060..2469bcb43b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/components-project", - "version": "7.0.0", + "version": "7.0.2", "description": "Netgrif Application Engine Frontend project. Project includes angular libraries as base for NAE applications.", "homepage": "https://components.netgrif.com", "license": "SEE LICENSE IN LICENSE", diff --git a/projects/netgrif-components-core/package.json b/projects/netgrif-components-core/package.json index e2e241993c..3524daef04 100644 --- a/projects/netgrif-components-core/package.json +++ b/projects/netgrif-components-core/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/components-core", - "version": "7.0.0", + "version": "7.0.2", "description": "Netgrif Application engine frontend core Angular library", "homepage": "https://components.netgrif.com", "license": "SEE LICENSE IN LICENSE", diff --git a/projects/netgrif-components/package.json b/projects/netgrif-components/package.json index 9cb7c2985f..3e3c62c3f8 100644 --- a/projects/netgrif-components/package.json +++ b/projects/netgrif-components/package.json @@ -1,6 +1,6 @@ { "name": "@netgrif/components", - "version": "7.0.0", + "version": "7.0.2", "description": "Netgrif Application Engine frontend Angular components", "homepage": "https://components.netgrif.com", "license": "SEE LICENSE IN LICENSE", @@ -29,7 +29,7 @@ "nae frontend" ], "peerDependencies": { - "@netgrif/components-core": "7.0.0", + "@netgrif/components-core": "7.0.2", "@angular-material-components/datetime-picker": "~16.0.0", "@angular-material-components/moment-adapter": "~16.0.0", "@angular/animations": "~17.1.0", From 6db3f17b4053fcfb8441e297b40894f41449b6e9 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 4 Aug 2026 14:17:44 +0200 Subject: [PATCH 12/16] Refactor tab switching to use unique IDs instead of indices Replaced index-based tracking with unique ID-based tracking for tab switching logic. This ensures more reliable identification of tabs and avoids potential issues with index mismatches. Adjusted all relevant methods to accommodate the use of unique IDs. --- .../src/lib/tabs/classes/tab-view.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts b/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts index 96612fc4f3..58e255db19 100644 --- a/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts +++ b/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts @@ -36,7 +36,7 @@ export class TabView implements TabViewInterface { private _switching = false; - private _pendingIndex: number; + private _pendingTabUniqueId: string | undefined; /** * @ignore @@ -279,7 +279,10 @@ export class TabView implements TabViewInterface { } public tabChange(event: MatTabChangeEvent) { - this._pendingIndex = event.index; + this._pendingTabUniqueId = this.openedTabs[event.index]?.uniqueId; + if (this._pendingTabUniqueId === undefined) { + return; + } if (this._switching) { return; // an update is already being processed; the pending value will be picked up after } @@ -288,9 +291,10 @@ export class TabView implements TabViewInterface { private _processSwitch() { this._switching = true; - const index = this._pendingIndex!; + const uniqueId = this._pendingTabUniqueId; + const index = this.openedTabs.findIndex(tab => tab.uniqueId === uniqueId); - if (index !== this.selectedIndex.value) { + if (index !== -1 && index !== this.selectedIndex.value) { let tab = this.openedTabs[this.selectedIndex.value]; if (tab) { tab.tabSelected$.next(false); @@ -304,7 +308,7 @@ export class TabView implements TabViewInterface { setTimeout(() => { this._switching = false; - if (this._pendingIndex !== index) { + if (this._pendingTabUniqueId !== uniqueId) { this._processSwitch(); } }, 150); From 757d792e8bde567094a39257b7469cef2ad9f794 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 4 Aug 2026 14:25:12 +0200 Subject: [PATCH 13/16] Refactor tabChange method to improve variable clarity Replaced direct assignment to the class property with a local variable for better readability and maintainability. Ensured functionality remains unchanged while improving code clarity. --- .../netgrif-components-core/src/lib/tabs/classes/tab-view.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts b/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts index 58e255db19..a81e28193d 100644 --- a/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts +++ b/projects/netgrif-components-core/src/lib/tabs/classes/tab-view.ts @@ -279,10 +279,11 @@ export class TabView implements TabViewInterface { } public tabChange(event: MatTabChangeEvent) { - this._pendingTabUniqueId = this.openedTabs[event.index]?.uniqueId; - if (this._pendingTabUniqueId === undefined) { + const pendingTabUniqueId = this.openedTabs[event.index]?.uniqueId; + if (pendingTabUniqueId === undefined) { return; } + this._pendingTabUniqueId = pendingTabUniqueId; if (this._switching) { return; // an update is already being processed; the pending value will be picked up after } From 2033c190ef4e271a8d2112e02f2b930ca34ad123 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 18 Aug 2026 09:29:19 +0200 Subject: [PATCH 14/16] Fix user comparison logic to use isAnonymous method Replaced the email-based check for anonymous users with the isAnonymous method. This change improves code readability and ensures consistent logic for user anonymity. --- .../src/lib/user/services/user-comparator.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/netgrif-components-core/src/lib/user/services/user-comparator.service.ts b/projects/netgrif-components-core/src/lib/user/services/user-comparator.service.ts index f2b12b586b..a96bbeb7a1 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user-comparator.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user-comparator.service.ts @@ -11,6 +11,6 @@ export class UserComparatorService { public compareUsers(userId: string, acceptImpersonator: boolean = true): boolean { const loggedUser = acceptImpersonator ? this._userService.user.getSelfOrImpersonated() : this._userService.user; - return userId === loggedUser.id || this._userService.user.email.includes('anonymous'); + return userId === loggedUser.id || this._userService.user.isAnonymous(); } } From c4c68c8afe60efad2607e31713275f6c83871f03 Mon Sep 17 00:00:00 2001 From: Machac Date: Wed, 9 Sep 2026 16:02:10 +0200 Subject: [PATCH 15/16] [NAE-2241] Anonymous access refactor - Implemented `loginWithApiToken` method in multiple services, including `proxyAuthentication.service`. - Enhanced `authentication-guard.service` to handle API token login with configurable query parameters. - Extended schema and configuration to include `apiToken` options for allowed paths, query parameters, and toggles. - Updated unit tests across services to validate API token login behavior. - Refactored session handling for consistency with token-based authentication. --- nae.json | 10 +++ .../schematics/_commons/schema.d.ts | 9 +++ .../src/commons/schema.ts | 9 +++ .../proxyAuthentication.service.spec.ts | 18 ++++- .../proxyAuthentication.service.ts | 22 ++++- .../services/authentication-interceptor.ts | 20 +++-- .../services/authentication-method.service.ts | 6 +- .../authentication.service.spec.ts | 8 ++ .../authentication/authentication.service.ts | 13 +++ .../authentication-guard.service.spec.ts | 69 +++++++++++++++- .../guard/authentication-guard.service.ts | 80 ++++++++++++++++++- .../session/services/session.service.ts | 40 +++++++--- .../lib/user/services/user.service.spec.ts | 9 +++ .../src/lib/user/services/user.service.ts | 11 +++ .../mock-authentication-method-service.ts | 4 + .../mocks/mock-authentication.service.ts | 4 + .../src/lib/utility/tests/test-config.ts | 9 ++- .../src/schema/nae-schema.json | 31 +++++++ 18 files changed, 342 insertions(+), 30 deletions(-) diff --git a/nae.json b/nae.json index f919d17fdd..86d7b49aa4 100644 --- a/nae.json +++ b/nae.json @@ -20,6 +20,16 @@ "sessionTimeoutEnabled": false, "sessionTimeout": 900, "jwtBearer": "X-Jwt-Token", + "apiToken": { + "enabled": true, + "queryParameter": "token", + "realmQueryParameter": "realmId", + "removeFromUrl": true, + "allowedPaths": [ + "/tabbed-views", + "/tabbed-views/**" + ] + }, "sso": { "enable": false, "clientId": "dev-cluster-worker", diff --git a/projects/netgrif-components-core/schematics/_commons/schema.d.ts b/projects/netgrif-components-core/schematics/_commons/schema.d.ts index 7b07799968..9f6b4956ec 100644 --- a/projects/netgrif-components-core/schematics/_commons/schema.d.ts +++ b/projects/netgrif-components-core/schematics/_commons/schema.d.ts @@ -47,11 +47,20 @@ export interface Auth { sessionBearer?: string; jwtEnabled?: boolean; jwtBearer?: string; + apiToken?: ApiTokenAuthentication; endpoints?: string | { [k: string]: string }; [k: string]: any; } +export interface ApiTokenAuthentication { + enabled?: boolean; + queryParameter?: string; + realmQueryParameter?: string; + removeFromUrl?: boolean; + allowedPaths?: Array; +} + export interface Resource { name: string; address: string; diff --git a/projects/netgrif-components-core/src/commons/schema.ts b/projects/netgrif-components-core/src/commons/schema.ts index 7a740b1b35..76afc0ce9b 100644 --- a/projects/netgrif-components-core/src/commons/schema.ts +++ b/projects/netgrif-components-core/src/commons/schema.ts @@ -45,12 +45,21 @@ export interface Auth { address: string; authentication: string; sessionBearer?: string; + apiToken?: ApiTokenAuthentication; endpoints?: string | { [k: string]: string }; sso?: Sso; [k: string]: any; } +export interface ApiTokenAuthentication { + enabled?: boolean; + queryParameter?: string; + realmQueryParameter?: string; + removeFromUrl?: boolean; + allowedPaths?: Array; +} + export interface Sso { enable: boolean; redirectUrl: string; diff --git a/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.spec.ts b/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.spec.ts index 5696e62298..a7fd123142 100644 --- a/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.spec.ts @@ -17,7 +17,8 @@ describe('ProxyAuthenticationService', () => { auth: { authentication: 'basic', address: 'http://localhost:8080', - endpoints: {login: '/api/auth/login'} + endpoints: {login: '/api/auth/login'}, + apiToken: {} } } } as any, @@ -59,4 +60,19 @@ describe('ProxyAuthenticationService', () => { expect(response).toBeTruthy(); })); + + it('authenticates an API token with bearer and realm headers', fakeAsync(() => { + let response: any; + + service.loginWithApiToken('user-id.secret', 'Admin').subscribe(res => response = res); + + const req = httpMock.expectOne('http://localhost:8080/api/auth/login'); + expect(req.request.method).toBe('GET'); + expect(req.request.headers.get('Authorization')).toBe('Bearer user-id.secret'); + expect(req.request.headers.get('X-Realm-ID')).toBe('Admin'); + req.flush({id: '1', name: 'User'}); + + tick(); + expect(response).toBeTruthy(); + })); }); diff --git a/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.ts b/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.ts index 666676abd4..368f26406c 100644 --- a/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.ts +++ b/projects/netgrif-components-core/src/lib/authentication/proxyAuthentication.service.ts @@ -1,12 +1,12 @@ import {ConfigurationService} from '../configuration/configuration.service'; import {NullAuthenticationService} from './services/methods/null-authentication/null-authentication.service'; import {BasicAuthenticationService} from './services/methods/basic-authentication/basic-authentication.service'; -import {HttpClient} from '@angular/common/http'; +import {HttpClient, HttpHeaders} from '@angular/common/http'; import {BasicWithRealmAuthenticationService} from "./services/methods/basic-authentication/basic-with-realm-authentication.service"; import {filter, take} from "rxjs/operators"; import {UserResource} from "../resources/interface/user-resource"; import {Credentials} from "./models/credentials"; -import {Observable} from 'rxjs'; +import {Observable, throwError} from 'rxjs'; import {Injectable} from "@angular/core"; import {AuthenticationMethodService} from "./services/authentication-method.service"; @@ -46,6 +46,24 @@ export class ProxyAuthenticationService extends AuthenticationMethodService { return this._proxyAuthMethod.login(credentials); } + loginWithApiToken(token: string, realmId?: string): Observable { + const auth = this._config.get().providers.auth; + const loginEndpoint = typeof auth.endpoints === 'object' ? auth.endpoints['login'] : undefined; + const url = auth.address + (loginEndpoint ?? ''); + if (!loginEndpoint) { + return throwError(new Error('Login URL is not defined in the config [nae.providers.auth.endpoints.login]')); + } + if (!token?.trim()) { + return throwError(new Error('API token is empty')); + } + + let headers = new HttpHeaders().set('Authorization', `Bearer ${token.trim()}`); + if (realmId?.trim()) { + headers = headers.set('X-Realm-ID', realmId.trim()); + } + return this._http.get(url, {headers}); + } + logout(): Observable { return this._proxyAuthMethod.logout(); } diff --git a/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts b/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts index 3e90a7f9b1..abc768a716 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts @@ -28,25 +28,31 @@ export class AuthenticationInterceptor implements HttpInterceptor { return next.handle(req); } - if (this._session && !!this._session.sessionToken) { + const sessionHeader = this._session.sessionHeader; + const sessionToken = req.headers.get(sessionHeader) || this._session.sessionToken || ''; + if (sessionToken && !req.headers.has('Authorization') && !req.headers.has(sessionHeader)) { req = req.clone({ - headers: req.headers.set(this._session.sessionHeader, this._session.sessionToken) + headers: req.headers.set(sessionHeader, sessionToken) }); this.idleTimerService.resetTimer(); } return next.handle(req).pipe( tap(event => { - if (event instanceof HttpResponse) { - if (event.headers.has(this._session.sessionHeader)) { - this._session.setVerifiedToken(event.headers.get(this._session.sessionHeader)); + if (event instanceof HttpResponse && (this._session.sessionToken || '') === sessionToken) { + const responseToken = event.headers.get(sessionHeader); + if (responseToken) { + this._session.setVerifiedToken(responseToken); } } }), catchError(errorEvent => { - if (errorEvent instanceof HttpErrorResponse && errorEvent.status === 401) { + if (errorEvent instanceof HttpErrorResponse && errorEvent.status === 401 + && (this._session.sessionToken || '') === sessionToken) { console.debug('Authentication token is invalid. Clearing session token'); this._session.clear(); - this._redirect.redirect(this._redirect.resolveLoginPath()); + if (this._session.isInitialized && !req.headers.has('Authorization')) { + this._redirect.redirect(this._redirect.resolveLoginPath()); + } } return throwError(errorEvent); }) diff --git a/projects/netgrif-components-core/src/lib/authentication/services/authentication-method.service.ts b/projects/netgrif-components-core/src/lib/authentication/services/authentication-method.service.ts index 240a4d00d9..49e71cce4d 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/authentication-method.service.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/authentication-method.service.ts @@ -1,4 +1,4 @@ -import {Observable} from 'rxjs'; +import {Observable, throwError} from 'rxjs'; import {Credentials} from '../models/credentials'; import {UserResource} from '../../resources/interface/user-resource'; @@ -9,5 +9,9 @@ export abstract class AuthenticationMethodService { abstract login(credentials: Credentials): Observable; + loginWithApiToken(_token: string, _realmId?: string): Observable { + return throwError(new Error('API token authentication is not supported by this authentication method')); + } + abstract logout(): Observable; } diff --git a/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.spec.ts b/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.spec.ts index 1705b1927b..da260e4df1 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.spec.ts @@ -40,6 +40,14 @@ describe('AuthenticationService', () => { }); }); + it('should login with an API token', (done) => { + service.loginWithApiToken('user-id.secret', 'Admin').subscribe(res => { + expect(res.id).toEqual('id'); + expect(service.isAuthenticated).toBe(true); + done(); + }); + }); + afterEach(() => { TestBed.resetTestingModule(); }); diff --git a/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.ts b/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.ts index f10233c2eb..a9feece659 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/authentication/authentication.service.ts @@ -49,6 +49,19 @@ export class AuthenticationService implements OnDestroy { ); } + loginWithApiToken(token: string, realmId?: string): Observable { + return this._auth.loginWithApiToken(token, realmId).pipe( + tap((user: UserResource) => { + this._authenticated$.next(!!user[AuthenticationService.IDENTIFICATION_ATTRIBUTE]); + }), + map((user: UserResource) => this._userTransformer.transform(user)), + catchError(error => { + console.error(error); + return of(null); + }) + ); + } + logout(): Observable { return this._auth.logout().pipe( tap(() => { diff --git a/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts b/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts index 45b5aa9283..6c4c5e84ee 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts @@ -1,4 +1,5 @@ -import {TestBed} from '@angular/core/testing'; +import {fakeAsync, TestBed, tick} from '@angular/core/testing'; +import {ActivatedRouteSnapshot, convertToParamMap, Router, RouterStateSnapshot, UrlTree} from '@angular/router'; import {ConfigurationService} from '../../../configuration/configuration.service'; import {AuthenticationMethodService} from '../authentication-method.service'; import {AuthenticationGuardService} from './authentication-guard.service'; @@ -9,11 +10,18 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MockAuthenticationMethodService} from '../../../utility/tests/mocks/mock-authentication-method-service'; import {MockAuthenticationService} from '../../../utility/tests/mocks/mock-authentication.service'; import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {SessionService} from '../../session/services/session.service'; +import {Observable, firstValueFrom, of} from 'rxjs'; +import {UserService} from '../../../user/services/user.service'; +import {User} from '../../../user/models/user'; describe('AuthenticationGuardService', () => { let service: AuthenticationGuardService; + let session: SessionService; + let userService: UserService; - beforeEach(() => { + beforeEach(fakeAsync(() => { + localStorage.removeItem(SessionService.SESSION_TOKEN_STORAGE_KEY); TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, @@ -28,13 +36,68 @@ describe('AuthenticationGuardService', () => { AuthenticationGuardService ]}); service = TestBed.inject(AuthenticationGuardService); - }); + session = TestBed.inject(SessionService); + userService = TestBed.inject(UserService); + tick(); + })); it('should be created', () => { expect(service).toBeTruthy(); }); + it('logs in from an allowed API-token URL and removes credentials from the URL', async () => { + const loginSpy = spyOn(userService, 'loginWithApiToken').and.callFake(() => { + session.setVerifiedToken('session-token'); + return of(user()); + }); + + const result = service.canActivate( + routeWithQuery({token: 'user-id.secret', realmId: 'Admin', caseId: 'case-1'}), + {url: '/tabbed-views?token=user-id.secret&realmId=Admin&caseId=case-1'} as RouterStateSnapshot + ) as Observable; + const decision = await firstValueFrom(result); + + expect(loginSpy).toHaveBeenCalledWith('user-id.secret', 'Admin'); + expect(decision instanceof UrlTree).toBeTrue(); + expect(TestBed.inject(Router).serializeUrl(decision as UrlTree)).toBe('/tabbed-views?caseId=case-1'); + }); + + it('does not consume an API token on a path outside the allow-list', () => { + const loginSpy = spyOn(userService, 'loginWithApiToken'); + const login = service.canActivate( + routeWithQuery({token: 'user-id.secret', realmId: 'Admin'}), + {url: '/login?token=user-id.secret&realmId=Admin'} as RouterStateSnapshot + ) as UrlTree; + + expect(loginSpy).not.toHaveBeenCalled(); + expect(TestBed.inject(Router).serializeUrl(login)).toBe('/login'); + }); + + it('keeps token query parameters when API-token login is disabled for an existing session', () => { + const configuration = TestBed.inject(ConfigurationService); + const value = configuration.get(); + value.providers.auth.apiToken = {...value.providers.auth.apiToken, enabled: false}; + spyOn(configuration, 'get').and.returnValue(value); + session.setVerifiedToken('session-token'); + + const result = service.canActivate( + routeWithQuery({token: 'application-value', realmId: 'application-realm'}), + {url: '/tabbed-views?token=application-value&realmId=application-realm'} as RouterStateSnapshot + ); + + expect(result).toBeTrue(); + }); + afterEach(() => { TestBed.resetTestingModule(); + localStorage.removeItem(SessionService.SESSION_TOKEN_STORAGE_KEY); }); }); + +function routeWithQuery(query: Record): ActivatedRouteSnapshot { + return {queryParamMap: convertToParamMap(query)} as ActivatedRouteSnapshot; +} + +function user(): User { + return new User('id', 'username', 'mail', 'Admin', 'name', 'surname', [], [], [], []); +} diff --git a/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.ts b/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.ts index bbb6491f22..476fff83a5 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.ts @@ -3,6 +3,11 @@ import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTre import {AuthenticationModule} from '../../authentication.module'; import {SessionService} from '../../session/services/session.service'; import {RedirectService} from '../../../routing/redirect-service/redirect.service'; +import {ConfigurationService} from '../../../configuration/configuration.service'; +import {UserService} from '../../../user/services/user.service'; +import {isObservable, Observable, of} from 'rxjs'; +import {catchError, filter, map, switchMap, take} from 'rxjs/operators'; +import {ApiTokenAuthentication} from '../../../../commons/schema'; @Injectable({ providedIn: AuthenticationModule @@ -13,12 +18,83 @@ export class AuthenticationGuardService implements CanActivate { constructor(private _session: SessionService, private _redirectService: RedirectService, + private _configuration: ConfigurationService, + private _userService: UserService, private _router: Router) { this._loginUrl = this._redirectService.resolveLoginPath(); } - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree { + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable { this._redirectService.intendedRoute = route; - return this._session.sessionToken && this._session.verified ? true : this._router.parseUrl(this._loginUrl); + if (this._session.isInitialized) { + return this.authorize(route, state); + } + return this._session.initializing.pipe( + filter(initialized => initialized), + take(1), + switchMap(() => { + const decision = this.authorize(route, state); + return isObservable(decision) ? decision : of(decision); + }) + ); + } + + private authorize(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable { + const config = this.apiTokenConfiguration(); + const tokenParameter = config?.queryParameter || 'token'; + const realmParameter = config?.realmQueryParameter || 'realmId'; + const token = route.queryParamMap.get(tokenParameter)?.trim(); + + if (this._session.sessionToken && this._session.verified) { + return config?.enabled && token && config.removeFromUrl !== false + ? this.sanitizedUrl(state.url, tokenParameter, realmParameter) + : true; + } + if (!config?.enabled || !token || !this.isAllowedPath(state.url, config.allowedPaths)) { + return this._router.parseUrl(this._loginUrl); + } + + const realmId = route.queryParamMap.get(realmParameter)?.trim(); + return this._userService.loginWithApiToken(token, realmId).pipe( + take(1), + map(user => { + if (!user || !this._session.sessionToken || !this._session.verified) { + return this._router.parseUrl(this._loginUrl); + } + return config.removeFromUrl === false + ? true + : this.sanitizedUrl(state.url, tokenParameter, realmParameter); + }), + catchError(() => of(this._router.parseUrl(this._loginUrl))) + ); + } + + private apiTokenConfiguration(): ApiTokenAuthentication | undefined { + return this._configuration.get().providers.auth.apiToken; + } + + private isAllowedPath(url: string, allowedPaths: Array | undefined): boolean { + if (!allowedPaths?.length) { + return false; + } + const path = this._router.parseUrl(url).root.children['primary']?.segments + .map(segment => segment.path) + .join('/') || ''; + const normalizedPath = `/${path}`.replace(/\/$/, '') || '/'; + return allowedPaths.some(allowedPath => { + const normalizedAllowedPath = `/${allowedPath}`.replace(/\/+/g, '/').replace(/\/$/, '') || '/'; + if (normalizedAllowedPath.endsWith('/**')) { + const prefix = normalizedAllowedPath.slice(0, -3); + return normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`); + } + return normalizedPath === normalizedAllowedPath; + }); + } + + private sanitizedUrl(url: string, tokenParameter: string, realmParameter: string): UrlTree { + const tree = this._router.parseUrl(url); + delete tree.queryParams[tokenParameter]; + delete tree.queryParams[realmParameter]; + return tree; } } diff --git a/projects/netgrif-components-core/src/lib/authentication/session/services/session.service.ts b/projects/netgrif-components-core/src/lib/authentication/session/services/session.service.ts index 61374d20bf..5ddd9c8c63 100644 --- a/projects/netgrif-components-core/src/lib/authentication/session/services/session.service.ts +++ b/projects/netgrif-components-core/src/lib/authentication/session/services/session.service.ts @@ -4,7 +4,7 @@ import {ConfigurationService} from '../../../configuration/configuration.service import {NullStorage} from '../null-storage'; import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http'; import {LoggerService} from '../../../logger/services/logger.service'; -import {catchError, filter, map, take, tap} from 'rxjs/operators'; +import {catchError, filter, finalize, map, take} from 'rxjs/operators'; import {MessageResource} from '../../../resources/interface/message-resource'; import {LoadingEmitter} from '../../../utility/loading-emitter'; import {SessionIdleTimerService} from "./session-idle-timer.service"; @@ -54,6 +54,7 @@ export class SessionService implements OnDestroy { ngOnDestroy(): void { this._session$.complete(); this._verifying.complete(); + this._initialized.complete(); } get session$(): Observable { @@ -61,6 +62,7 @@ export class SessionService implements OnDestroy { } set sessionToken(sessionToken: string) { + this.ensureConfigInitialized(); this._session$.next(sessionToken); this._storage.setItem(SessionService.SESSION_TOKEN_STORAGE_KEY, btoa(SessionService.SESSION_TOKEN_STORAGE_KEY + ':' + sessionToken)); @@ -131,41 +133,48 @@ export class SessionService implements OnDestroy { observe: 'response' }).pipe( catchError(error => { - if (error instanceof HttpErrorResponse && error.status === 401) { + if (error instanceof HttpErrorResponse && error.status === 401 && this.sessionToken === token) { this._log.warn('Authentication token is invalid. Clearing session token'); this.clear(); } - this._verifying.off(); - this.idleTimerService.stopTimer(); - this._initialized.on(); return throwError(error); }), map(response => { + if (this.sessionToken !== token) { + return this.verified; + } this._log.debug(response.body.success); this._verified = true; this.idleTimerService.resetTimer(); - this._initialized.on(); - this.sessionToken = token; + this.sessionToken = response.headers.get(this.sessionHeader) || token; return true; }), - tap(_ => this._verifying.off()) + finalize(() => { + this._verifying.off(); + this._initialized.on(); + }) ); } } protected load(): string { this.ensureConfigInitialized(); + if (this.verified && this.sessionToken) { + this._initialized.on(); + return this.sessionToken; + } - let token = this._storage.getItem(SessionService.SESSION_TOKEN_STORAGE_KEY); + const token = this.resolveToken(this._storage.getItem(SessionService.SESSION_TOKEN_STORAGE_KEY)); this._verified = false; this.idleTimerService.stopTimer(); if (token) { - token = this.resolveToken(token); this.sessionToken = token; - this.verify(token).pipe(take(1)).subscribe(ver => { - this._log.debug('Token ' + token + ' verified status: ' + ver); + this.verify(token).pipe(take(1)).subscribe({ + next: verified => this._log.debug('Stored session verified: ' + verified), + error: () => this._log.warn('Stored session could not be restored') }); } else { + this.clear(); this._initialized.on(); } return ''; @@ -184,7 +193,12 @@ export class SessionService implements OnDestroy { } private resolveToken(raw: string): string { - return raw ? atob(raw).split(':')[1] : ''; + try { + const [key, token] = raw ? atob(raw).split(':') : []; + return key === SessionService.SESSION_TOKEN_STORAGE_KEY ? token || '' : ''; + } catch { + return ''; + } } private resolveStorage(storage: string): any { diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.spec.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.spec.ts index e79ef9385b..ab3422f05c 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.spec.ts @@ -40,6 +40,15 @@ describe('UserService', () => { }); }); + it('should login with an API token', (done) => { + service.loginWithApiToken('user-id.secret', 'Admin').subscribe(res => { + expect(res.id).toEqual('id'); + expect(service.user.id).toEqual('id'); + expect(service.hasAuthority('ADMIN')).toBeTrue(); + done(); + }); + }); + it('should logout', (done) => { service.logout().subscribe(res => { expect(res).toEqual(undefined); diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.ts index d808240288..97ab49e020 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.ts @@ -153,6 +153,17 @@ export class UserService implements OnDestroy { ); } + public loginWithApiToken(token: string, realmId?: string): Observable { + this._loginCalled = true; + return this._authService.loginWithApiToken(token, realmId).pipe( + tap((authUser: User) => { + this._user = authUser; + this._loginCalled = false; + this.publishUserChange(); + }) + ); + } + public logout(): Observable { return this._authService.logout().pipe( tap(() => { diff --git a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts index 3bf1ab5798..02e8eb804b 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts @@ -9,6 +9,10 @@ export class MockAuthenticationMethodService extends AuthenticationMethodService groups: [], authorities: [], nextGroups: [], processRoles: []}); } + loginWithApiToken(_token: string, _realmId?: string): Observable { + return this.login({username: '', password: ''}); + } + logout(): Observable { return of(undefined); } diff --git a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts index e97d420640..aca54ed193 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts @@ -23,6 +23,10 @@ export class MockAuthenticationService extends AuthenticationService { return of(new User('id', 'username', 'mail', 'realmId', 'name', 'surname', ['ADMIN'], [{stringId: 'id', name: 'id', importId: 'id'}])); } + loginWithApiToken(_token: string, _realmId?: string): Observable { + return this.login({username: '', password: ''}); + } + logout(): Observable { return of(undefined); } diff --git a/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts b/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts index 18923cb419..503af036f0 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts @@ -30,7 +30,14 @@ export class TestConfigurationService extends ConfigurationService { }, sessionBearer: 'X-Auth-Token', sessionTimeoutEnabled: false, - sessionTimeout: 900 + sessionTimeout: 900, + apiToken: { + enabled: true, + queryParameter: 'token', + realmQueryParameter: 'realmId', + removeFromUrl: true, + allowedPaths: ['/tabbed-views', '/tabbed-views/**'] + } }, resources: [ { diff --git a/projects/netgrif-components-core/src/schema/nae-schema.json b/projects/netgrif-components-core/src/schema/nae-schema.json index ac45d84f42..06eae8593c 100644 --- a/projects/netgrif-components-core/src/schema/nae-schema.json +++ b/projects/netgrif-components-core/src/schema/nae-schema.json @@ -35,6 +35,9 @@ "authentication": { "type": "string" }, + "apiToken": { + "$ref": "#/definitions/ApiTokenAuthentication" + }, "endpoints": { "anyOf": [ { @@ -62,6 +65,34 @@ }, "type": "object" }, + "ApiTokenAuthentication": { + "additionalProperties": false, + "properties": { + "allowedPaths": { + "items": { + "type": "string" + }, + "type": "array" + }, + "enabled": { + "default": false, + "type": "boolean" + }, + "queryParameter": { + "default": "token", + "type": "string" + }, + "realmQueryParameter": { + "default": "realmId", + "type": "string" + }, + "removeFromUrl": { + "default": true, + "type": "boolean" + } + }, + "type": "object" + }, "CaseLayout": { "properties": { "name": { From 10b4a243b43b6aa0160203211e39ab2a51cb01c3 Mon Sep 17 00:00:00 2001 From: Machac Date: Wed, 9 Sep 2026 17:20:35 +0200 Subject: [PATCH 16/16] [NAE-2241] Anonymous access refactor - Implemented `loginWithApiToken` method in multiple services, including `proxyAuthentication.service`. - Enhanced `authentication-guard.service` to handle API token login with configurable query parameters. - Extended schema and configuration to include `apiToken` options for allowed paths, query parameters, and toggles. - Updated unit tests across services to validate API token login behavior. - Refactored session handling for consistency with token-based authentication. --- .../authentication-guard.service.spec.ts | 5 +++-- ...act-button-default-field.component.spec.ts | 3 ++- .../abstract-number-errors.component.spec.ts | 1 + .../src/lib/utility/tests/test-config.ts | 22 ++++++++++++------- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts b/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts index 6c4c5e84ee..4d0425f426 100644 --- a/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/authentication/services/guard/authentication-guard.service.spec.ts @@ -5,7 +5,7 @@ import {AuthenticationMethodService} from '../authentication-method.service'; import {AuthenticationGuardService} from './authentication-guard.service'; import {AuthenticationService} from '../authentication/authentication.service'; import {RouterTestingModule} from '@angular/router/testing'; -import {TestConfigurationService} from '../../../utility/tests/test-config'; +import {ApiTokenTestConfigurationService} from '../../../utility/tests/test-config'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MockAuthenticationMethodService} from '../../../utility/tests/mocks/mock-authentication-method-service'; import {MockAuthenticationService} from '../../../utility/tests/mocks/mock-authentication.service'; @@ -22,6 +22,7 @@ describe('AuthenticationGuardService', () => { beforeEach(fakeAsync(() => { localStorage.removeItem(SessionService.SESSION_TOKEN_STORAGE_KEY); + const configuration = new ApiTokenTestConfigurationService(); TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, @@ -30,7 +31,7 @@ describe('AuthenticationGuardService', () => { RouterTestingModule.withRoutes([]) ], providers: [ - {provide: ConfigurationService, useClass: TestConfigurationService}, + {provide: ConfigurationService, useValue: configuration}, {provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService}, {provide: AuthenticationService, useClass: MockAuthenticationService}, AuthenticationGuardService diff --git a/projects/netgrif-components-core/src/lib/data-fields/button-field/button-default-field/abstract-button-default-field.component.spec.ts b/projects/netgrif-components-core/src/lib/data-fields/button-field/button-default-field/abstract-button-default-field.component.spec.ts index da7d5d798d..cb4e3a0e42 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/button-field/button-default-field/abstract-button-default-field.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/button-field/button-default-field/abstract-button-default-field.component.spec.ts @@ -57,7 +57,8 @@ describe('AbstractButtonDefaultFieldComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(TestWrapperComponent); - const initializeLanguage = TestBed.inject(LanguageService); + TestBed.inject(LanguageService); + TestBed.inject(TranslateService).use('en'); component = fixture.debugElement.children[0].componentInstance; fixture.detectChanges(); })); diff --git a/projects/netgrif-components-core/src/lib/data-fields/number-field/abstract-number-errors.component.spec.ts b/projects/netgrif-components-core/src/lib/data-fields/number-field/abstract-number-errors.component.spec.ts index 54dfedb66b..d91f2a80cd 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/number-field/abstract-number-errors.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/number-field/abstract-number-errors.component.spec.ts @@ -70,6 +70,7 @@ describe('AbstractNumberErrorsComponent', () => { fixture = TestBed.createComponent(TestWrapperComponent); component = fixture.debugElement.children[0].componentInstance; TestBed.inject(LanguageService); + TestBed.inject(TranslateService).use('en'); fixture.detectChanges(); })); diff --git a/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts b/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts index 503af036f0..00cf12cb3d 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/test-config.ts @@ -30,14 +30,7 @@ export class TestConfigurationService extends ConfigurationService { }, sessionBearer: 'X-Auth-Token', sessionTimeoutEnabled: false, - sessionTimeout: 900, - apiToken: { - enabled: true, - queryParameter: 'token', - realmQueryParameter: 'realmId', - removeFromUrl: true, - allowedPaths: ['/tabbed-views', '/tabbed-views/**'] - } + sessionTimeout: 900 }, resources: [ { @@ -454,6 +447,19 @@ export class TestConfigurationService extends ConfigurationService { } } +export class ApiTokenTestConfigurationService extends TestConfigurationService { + constructor() { + super(); + this.configuration.providers.auth.apiToken = { + enabled: true, + queryParameter: 'token', + realmQueryParameter: 'realmId', + removeFromUrl: true, + allowedPaths: ['/tabbed-views', '/tabbed-views/**'] + }; + } +} + class TestHttp extends HttpHandler { handle(req: HttpRequest): Observable> { return undefined;