From 9cdebe01d7ea650ecd3154e7f3d3549db077adac Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 31 Oct 2025 14:16:12 +0100 Subject: [PATCH 01/10] 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/10] 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/10] 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/10] [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/10] [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/10] [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/10] [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 2033c190ef4e271a8d2112e02f2b930ca34ad123 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 18 Aug 2026 09:29:19 +0200 Subject: [PATCH 08/10] 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 09/10] [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 10/10] [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;