Skip to content

Commit 4fcfabc

Browse files
author
Diogo Ferraz
committed
fix(auth-routing): require explicit preview opt-in, add unauthorized route handling, and normalize error-page redirects
1 parent 427654c commit 4fcfabc

22 files changed

Lines changed: 125 additions & 85 deletions

File tree

src/app/app.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export class AppComponent {
100100
return currentUrl === '/' ||
101101
currentUrl.startsWith('/login') ||
102102
currentUrl.startsWith('/callback') ||
103-
currentUrl.startsWith('/not-found');
103+
currentUrl.startsWith('/not-found') ||
104+
currentUrl.startsWith('/unauthorized');
104105
}
105106

106107
ngOnDestroy() {

src/app/app.routes.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { authGuard } from './core/auth/guards/auth.guard';
1212
import { AuthCallbackComponent } from './features/login/components/auth-callback/auth-callback.component';
1313
import { LandingPageComponent } from './features/landing/components/landing-page/landing-page.component';
1414
import { NotFoundComponent } from './features/errors/components/not-found/not-found.component';
15+
import { UnauthorizedComponent } from './features/errors/components/unauthorized/unauthorized.component';
1516
import { SearchFiltersComponent } from './features/search/components/search-filters/search-filters.component';
1617
import { ProjectDocsComponent } from './features/docs/components/project-docs/project-docs.component';
1718
import { TaskCalendarComponent } from './features/calendar/components/task-calendar/task-calendar.component';
@@ -45,6 +46,8 @@ export const routes: Routes = [
4546
{ path: 'tasks/my-tasks', component: UserTaskItemsComponent, canActivate: [authGuard] },
4647
{ path: 'login', component: HomeLoginComponent },
4748
{ path: 'callback', component: AuthCallbackComponent },
49+
{ path: 'notfound', redirectTo: 'not-found' },
4850
{ path: 'not-found', component: NotFoundComponent },
49-
{ path: '**', component: NotFoundComponent }
51+
{ path: 'unauthorized', component: UnauthorizedComponent },
52+
{ path: '**', redirectTo: 'not-found' }
5053
];

src/app/core/auth/guards/admin-role.guard.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('adminRoleGuard', () => {
2525
expect(routerMock.createUrlTree).not.toHaveBeenCalled();
2626
});
2727

28-
it('redirects non-admin users to dashboard', () => {
28+
it('redirects non-admin users to unauthorized', () => {
2929
const redirectTree = {} as never;
3030
const authServiceMock = {
3131
hasRole: () => false
@@ -44,6 +44,6 @@ describe('adminRoleGuard', () => {
4444

4545
const canActivate = TestBed.runInInjectionContext(() => adminRoleGuard({} as never, {} as never));
4646
expect(canActivate).toBe(redirectTree);
47-
expect(routerMock.createUrlTree).toHaveBeenCalledWith(['/dashboard']);
47+
expect(routerMock.createUrlTree).toHaveBeenCalledWith(['/unauthorized']);
4848
});
4949
});

src/app/core/auth/guards/admin-role.guard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ export const adminRoleGuard: CanActivateFn = () => {
1010
return true;
1111
}
1212

13-
return router.createUrlTree(['/dashboard']);
13+
return router.createUrlTree(['/unauthorized']);
1414
};
Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { TestBed } from '@angular/core/testing';
2+
import { Router } from '@angular/router';
23
import { AuthService } from '../services/auth.service';
34
import { authGuard } from './auth.guard';
45

@@ -7,50 +8,69 @@ describe('authGuard', () => {
78
const authServiceMock = {
89
isAuthenticated: () => true,
910
authSession: () => null,
10-
canStartDebugSession: () => false,
11-
startLoginRedirect: jasmine.createSpy('startLoginRedirect').and.resolveTo()
11+
canStartDebugSession: () => false
12+
};
13+
14+
const routerMock = {
15+
createUrlTree: jasmine.createSpy('createUrlTree')
1216
};
1317

1418
TestBed.configureTestingModule({
15-
providers: [{ provide: AuthService, useValue: authServiceMock }]
19+
providers: [
20+
{ provide: AuthService, useValue: authServiceMock },
21+
{ provide: Router, useValue: routerMock }
22+
]
1623
});
1724

1825
const canActivate = TestBed.runInInjectionContext(() => authGuard({} as never, {} as never));
1926
expect(canActivate).toBeTrue();
20-
expect(authServiceMock.startLoginRedirect).not.toHaveBeenCalled();
27+
expect(routerMock.createUrlTree).not.toHaveBeenCalled();
2128
});
2229

23-
it('starts login redirect when user is not authenticated', () => {
30+
it('redirects to unauthorized when user is not authenticated', () => {
31+
const redirectTree = {} as never;
2432
const authServiceMock = {
2533
isAuthenticated: () => false,
2634
authSession: () => null,
27-
canStartDebugSession: () => false,
28-
startLoginRedirect: jasmine.createSpy('startLoginRedirect').and.resolveTo()
35+
canStartDebugSession: () => false
36+
};
37+
38+
const routerMock = {
39+
createUrlTree: jasmine.createSpy('createUrlTree').and.returnValue(redirectTree)
2940
};
3041

3142
TestBed.configureTestingModule({
32-
providers: [{ provide: AuthService, useValue: authServiceMock }]
43+
providers: [
44+
{ provide: AuthService, useValue: authServiceMock },
45+
{ provide: Router, useValue: routerMock }
46+
]
3347
});
3448

3549
const canActivate = TestBed.runInInjectionContext(() => authGuard({} as never, {} as never));
36-
expect(canActivate).toBeFalse();
37-
expect(authServiceMock.startLoginRedirect).toHaveBeenCalled();
50+
expect(canActivate).toBe(redirectTree);
51+
expect(routerMock.createUrlTree).toHaveBeenCalledWith(['/unauthorized']);
3852
});
3953

4054
it('allows navigation when debug session is active in preview mode', () => {
4155
const authServiceMock = {
4256
isAuthenticated: () => false,
4357
authSession: () => ({ isDebugSession: true }),
44-
canStartDebugSession: () => true,
45-
startLoginRedirect: jasmine.createSpy('startLoginRedirect').and.resolveTo()
58+
canStartDebugSession: () => true
59+
};
60+
61+
const routerMock = {
62+
createUrlTree: jasmine.createSpy('createUrlTree')
4663
};
4764

4865
TestBed.configureTestingModule({
49-
providers: [{ provide: AuthService, useValue: authServiceMock }]
66+
providers: [
67+
{ provide: AuthService, useValue: authServiceMock },
68+
{ provide: Router, useValue: routerMock }
69+
]
5070
});
5171

5272
const canActivate = TestBed.runInInjectionContext(() => authGuard({} as never, {} as never));
5373
expect(canActivate).toBeTrue();
54-
expect(authServiceMock.startLoginRedirect).not.toHaveBeenCalled();
74+
expect(routerMock.createUrlTree).not.toHaveBeenCalled();
5575
});
5676
});
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { CanActivateFn } from '@angular/router';
1+
import { CanActivateFn, Router } from '@angular/router';
22
import { inject } from '@angular/core';
33
import { AuthService } from '../services/auth.service';
44

55
export const authGuard: CanActivateFn = () => {
66
const authService = inject(AuthService);
7+
const router = inject(Router);
78

89
const debugSession = authService.authSession();
910
if (debugSession?.isDebugSession && authService.canStartDebugSession()) {
@@ -14,6 +15,5 @@ export const authGuard: CanActivateFn = () => {
1415
return true;
1516
}
1617

17-
void authService.startLoginRedirect();
18-
return false;
18+
return router.createUrlTree(['/unauthorized']);
1919
};

src/app/core/auth/guards/manager-or-admin.guard.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,5 @@ export const managerOrAdminGuard: CanActivateFn = () => {
1010
return true;
1111
}
1212

13-
return router.createUrlTree(['/dashboard']);
13+
return router.createUrlTree(['/unauthorized']);
1414
};
15-

src/app/features/activity/components/activity-log/activity-log.component.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,7 @@ export class ActivityLogComponent implements OnInit, OnDestroy {
187187
}
188188

189189
private shouldUsePreviewMode(): boolean {
190-
if (!this.appEnvironment.production) {
191-
return true;
192-
}
193-
194-
return this.authService.authSession()?.isDebugSession === true;
190+
return this.authService.authSession()?.isDebugSession === true && this.authService.canStartDebugSession();
195191
}
196192

197193
private loadPreviewData(detail: string): void {
@@ -305,4 +301,3 @@ export class ActivityLogComponent implements OnInit, OnDestroy {
305301
}
306302
}
307303
}
308-

src/app/features/activity/components/my-activity/my-activity.component.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,7 @@ export class MyActivityComponent implements OnInit, OnDestroy {
217217
}
218218

219219
private shouldUsePreviewMode(): boolean {
220-
if (!this.appEnvironment.production) {
221-
return true;
222-
}
223-
224-
return this.authService.authSession()?.isDebugSession === true;
220+
return this.authService.authSession()?.isDebugSession === true && this.authService.canStartDebugSession();
225221
}
226222

227223
private loadPreviewActivity(detail: string): void {
@@ -305,4 +301,3 @@ export class MyActivityComponent implements OnInit, OnDestroy {
305301
return value.replace(/<[^>]*>/g, ' ');
306302
}
307303
}
308-

src/app/features/admin/components/admin-dashboard/admin-dashboard.component.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,7 @@ export class AdminDashboardComponent implements OnInit {
275275
}
276276

277277
private shouldUsePreviewMode(): boolean {
278-
if (!this.appEnvironment.production) {
279-
return true;
280-
}
281-
282-
return this.authService.authSession()?.isDebugSession === true;
278+
return this.authService.authSession()?.isDebugSession === true && this.authService.canStartDebugSession();
283279
}
284280

285281
private loadPreviewUsers(detail: string): void {

0 commit comments

Comments
 (0)