Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion projects/nae-example-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
// });
// });
//
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if AnonymousService still exists and is used in the codebase

echo "=== Checking if AnonymousService class exists ==="
rg -l "class AnonymousService" --type ts

echo -e "\n=== Checking for imports/usages of AnonymousService ==="
rg -n "AnonymousService" --type ts -g '!*.spec.ts' | head -30

echo -e "\n=== Checking if anonymous.service.ts exists and its content ==="
fd "anonymous.service.ts" --type f -x cat -n {}

echo -e "\n=== Checking if the spec file is the only file commented out ==="
ls -la "projects/netgrif-components-core/src/lib/authentication/anonymous/"

Repository: netgrif/components

Length of output: 4437


Restore active tests for AnonymousService.

The AnonymousService is still active and fully implemented, but its test file is entirely commented out, leaving the service without test coverage. Restore the test suite to maintain coverage for the active service.

🤖 Prompt for AI Agents
In
`@projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.spec.ts`
around lines 1 - 28, Uncomment and restore the AnonymousService unit tests so
the service has coverage: re-enable the import statements (TestBed,
NoopAnimationsModule, HttpClientTestingModule, RouterTestingModule,
AnonymousService, ConfigurationService, TestConfigurationService) and restore
the describe block that creates the TestBed, injects AnonymousService in
beforeEach, asserts expect(service).toBeTruthy() in the it('should be created')
test, and calls TestBed.resetTestingModule() in afterEach; ensure the
TestBed.configureTestingModule provides {provide: ConfigurationService,
useClass: TestConfigurationService} and imports NoopAnimationsModule,
HttpClientTestingModule and RouterTestingModule.withRoutes([]).

Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,43 @@ 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<boolean>;

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<boolean>(false);
}

get jwtHeader(): string {
return this._jwtHeader;
get anonymousTokenHeader(): string {
return this._anonymousTokenHeader;
}

get tokenSet(): Observable<boolean> {
return this._tokenSet.asObservable();
}

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();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use resolved storage in ngOnDestroy.

ngOnDestroy always clears localStorage, ignoring session/null storage settings and leaving tokens behind.

🛠️ Proposed fix
-        localStorage.removeItem(this._anonymousTokenHeader);
+        this._storage.removeItem(this._anonymousTokenHeader);
🤖 Prompt for AI Agents
In
`@projects/netgrif-components-core/src/lib/authentication/anonymous/anonymous.service.ts`
around lines 47 - 49, ngOnDestroy currently always calls localStorage.removeItem
and thus ignores the resolved storage strategy; update ngOnDestroy in
AnonymousService to remove the token via the resolved storage instance/method
used elsewhere (e.g. use the same storage accessor/property used when
setting/getting the token instead of localStorage directly), and then complete
the _tokenSet as before; reference ngOnDestroy, _anonymousTokenHeader and
_tokenSet so you replace localStorage.removeItem(this._anonymousTokenHeader)
with the equivalent call on the resolved storage (or storage service) used
across the class.

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Remove the commented export from the public API surface.

Keeping commented exports in a public API file creates dead code and ambiguity. If this export is intentionally retired, delete it outright.

🧹 Suggested cleanup
-// export * from './services/anonymous-authentication-interceptor'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// export * from './services/anonymous-authentication-interceptor'
🤖 Prompt for AI Agents
In `@projects/netgrif-components-core/src/lib/authentication/public-api.ts` at
line 13, Remove the commented dead export in public-api.ts: delete the line "//
export * from './services/anonymous-authentication-interceptor'". This cleans up
the public API surface by removing the stale commented export referencing the
anonymous-authentication-interceptor module.

export * from './services/authentication-interceptor'
export * from './proxyAuthentication.service'

Expand Down
Original file line number Diff line number Diff line change
@@ -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();
// });
// });
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -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<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const jwtAuthToken = this._anonymousService.getToken();

if (!this._anonymousService) {
next.handle(req);
}

if (!!jwtAuthToken) {
req = req.clone({
headers: req.headers.set(this._anonymousService.jwtHeader, jwtAuthToken)
});
}
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));
}
}
}),
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<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 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);
// })
// );
// }
// }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ 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';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove commented-out import.

Delete the commented-out import rather than leaving dead code. If the AnonymousService dependency is permanently removed from this interceptor, clean up the remnants.

♻️ Proposed fix
-// import {AnonymousService} from '../anonymous/anonymous.service';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// import {AnonymousService} from '../anonymous/anonymous.service';
🤖 Prompt for AI Agents
In
`@projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts`
at line 14, Remove the dead commented import for AnonymousService from
authentication-interceptor.ts and clean up any remaining references or leftover
comments related to AnonymousService in the AuthenticationInterceptor (e.g.,
constructor parameters, private fields, or TODO comments) so the file contains
only active imports and dependencies; ensure the import block and any unused
symbols in the AuthenticationInterceptor class are cleaned up to avoid linter
warnings.

import {SessionIdleTimerService} from "../session/services/session-idle-timer.service";

@Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {

constructor(private _session: SessionService,
private _redirect: RedirectService,
private _anonymousService: AnonymousService,
// private _anonymousService: AnonymousService,
private idleTimerService: SessionIdleTimerService) {
}
Comment on lines 20 to 24
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove commented-out constructor parameter.

The commented-out _anonymousService parameter should be deleted for cleanliness.

♻️ Proposed fix
 constructor(private _session: SessionService,
             private _redirect: RedirectService,
-            // private _anonymousService: AnonymousService,
             private idleTimerService: SessionIdleTimerService) {
 }
🤖 Prompt for AI Agents
In
`@projects/netgrif-components-core/src/lib/authentication/services/authentication-interceptor.ts`
around lines 20 - 24, Remove the commented-out constructor parameter
_anonymousService from the AuthenticationInterceptor constructor: open the
constructor in authentication-interceptor.ts (the constructor method that
currently lists _session, _redirect, // private _anonymousService:
AnonymousService, and idleTimerService) and delete the commented line so the
constructor only declares the active dependencies (_session, _redirect,
idleTimerService).


Expand All @@ -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)) {
this._session.setVerifiedToken(event.headers.get(this._session.sessionHeader));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions projects/netgrif-components-core/src/lib/filter/models/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading