Skip to content

Commit

Permalink
Merge branch 'feature/tapisv3' into hotfix/add-prj-id-to-project-listing
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanfranklin authored Jul 9, 2024
2 parents 12cf956 + ddd2ada commit adb862b
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 90 deletions.
2 changes: 1 addition & 1 deletion angular/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const routes: Routes = [
},
],
},
{ path: 'callback', component: CallbackComponent },
{ path: 'handle-login', component: CallbackComponent },
{ path: 'streetview/callback', component: StreetviewCallbackComponent },
{ path: '404', component: NotFoundComponent },
{ path: '**', redirectTo: '', pathMatch: 'full' },
Expand Down
52 changes: 17 additions & 35 deletions angular/src/app/app.interceptors.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './services/authentication.service';
import { EnvService } from './services/env.service';
import { catchError } from 'rxjs/operators';
import { StreetviewAuthenticationService } from './services/streetview-authentication.service';
import { NotificationsService } from './services/notifications.service';
import { v4 as uuidv4 } from 'uuid';
import { LOGIN } from './constants/routes';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private authSvc: AuthService,
private envService: EnvService,
private streetviewAuthService: StreetviewAuthenticationService
) {}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// TODO_TAPISV3 put the tapis url in envService
const isTargetUrl =
request.url.includes(this.envService.tapisUrl) ||
request.url.includes(this.envService.apiUrl) ||
request.url.includes(this.envService.designSafeUrl);
if (isTargetUrl && this.authSvc.isLoggedIn()) {
// add tapis token to Geoapi or Tapis requests
request = request.clone({
setHeaders: {
'X-Tapis-Token': this.authSvc.userToken.token,
},
});
if (isTargetUrl) {
if (this.authSvc.isLoggedInButTokenExpired()) {
// check for an expired user token and get user to relogin if expired
this.router.navigateByUrl(LOGIN + '?to=' + encodeURIComponent(this.router.url));
}

if (this.authSvc.isLoggedIn()) {
// add tapis token to Geoapi or Tapis requests
request = request.clone({
setHeaders: {
'X-Tapis-Token': this.authSvc.userToken.token,
},
});
}
}

if (request.url.indexOf(this.envService.apiUrl) > -1) {
Expand Down Expand Up @@ -99,28 +106,3 @@ export class JwtInterceptor implements HttpInterceptor {
return request.method === 'GET' && urlPattern.test(request.url);
}
}

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(
private authService: AuthService,
private envService: EnvService,
private streetviewAuthService: StreetviewAuthenticationService,
private notificationService: NotificationsService
) {}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((err) => {
if (err.status === 401) {
// auto logout if 401 response returned from api
// https://jira.tacc.utexas.edu/browse/DES-1999
// TODO_TAPISV3 renable these
// this.authService.logout();
// location.reload();
}
throw err;
})
);
}
}
7 changes: 1 addition & 6 deletions angular/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { AuthService } from './services/authentication.service';
import { ModalService } from './services/modal.service';
import { EnvService } from './services/env.service';
import { CallbackComponent } from './components/callback/callback.component';
import { AuthInterceptor, JwtInterceptor } from './app.interceptors';
import { JwtInterceptor } from './app.interceptors';
import { ModalCreateProjectComponent } from './components/modal-create-project/modal-create-project.component';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { ModalFileBrowserComponent } from './components/modal-file-browser/modal-file-browser.component';
Expand Down Expand Up @@ -154,11 +154,6 @@ import { QuestionnaireDetailComponent } from './components/questionnaire-detail/
useClass: JwtInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
multi: true,
useClass: AuthInterceptor,
},
{
provide: CDK_DRAG_CONFIG,
useValue: {
Expand Down
2 changes: 0 additions & 2 deletions angular/src/app/components/callback/callback.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export class CallbackComponent implements OnInit {
constructor(private route: ActivatedRoute, private auth: AuthService) {}

ngOnInit() {
const frag = this.route.snapshot.fragment;
const params = new URLSearchParams(frag);
const token = this.route.snapshot.queryParams.access_token;
const expires_in = this.route.snapshot.queryParams.expires_in;
this.auth.setToken(token, expires_in);
Expand Down
24 changes: 13 additions & 11 deletions angular/src/app/components/file-browser/file-browser.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,19 @@ export class FileBrowserComponent implements OnInit {

this.currentPath.next(this.currentDirectory.path);

// Add '..' entry for users to move to parent path
const backPath = {
name: '..',
format: 'folder',
type: 'dir',
mimeType: 'test/directory',
size: 8192,
path: this.tapisFilesService.getParentPath(this.currentDirectory.path),
system: this.currentDirectory.system,
};
files.unshift(backPath);
// If this is the first load, add the '..' entry for users to move to parent path
if (this.offset === 0) {
const backPath = {
name: '..',
format: 'folder',
type: 'dir',
mimeType: 'test/directory',
size: 8192,
path: this.tapisFilesService.getParentPath(this.currentDirectory.path),
system: this.currentDirectory.system,
};
this.filesList.unshift(backPath);
}

this.inProgress = false;
this.filesList = this.filesList.concat(files);
Expand Down
12 changes: 9 additions & 3 deletions angular/src/app/models/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,25 @@ export class AuthToken {
this.expires = new Date(expires);
}

/** Creates an AuthToken instance from a token and an expiration time in seconds. */
static fromExpiresIn(token: string, expires_in: number) {
const expires = new Date(new Date().getTime() + expires_in * 1000);
return new AuthToken(token, expires);
}

/**
* Checks if the token is expired or not
* Checks if the token is expired or not.
* A 5 minute buffer is used to consider a token as expired slightly before its actual expiration time.
* @returns True if the token is expired, false otherwise.
*/
public isExpired(): boolean {
const buffer = 300000; // 5 minutes in milliseconds
if (this.expires) {
return new Date().getTime() > this.expires.getTime();
// Subtract buffer from the expiration time and compare with the current time
return new Date().getTime() > this.expires.getTime() - buffer;
} else {
return false;
// If expires is not set, consider the token as not expired
return false; // TODO_V3 this affects the streetview token; should be confirmed or refactored.
}
}
}
Expand Down
49 changes: 19 additions & 30 deletions angular/src/app/services/authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,22 @@ export class AuthService {
constructor(private http: HttpClient, private envService: EnvService, private router: Router) {}

public getTokenKeyword() {
return `${this.envService.env}HazmapperToken`;
return `${this.envService.env}HazmapperV3Token`;
}

public getRedirectKeyword() {
return `${this.envService.env}HazmapperRedirect`;
}

public login(requestedUrl: string) {
this.logout();
localStorage.setItem(this.getRedirectKeyword(), requestedUrl);

// First, check if the user has a token in localStorage
const tokenStr = localStorage.getItem(this.getTokenKeyword());
if (!tokenStr) {
this.redirectToAuthenticator();
} else {
const token = JSON.parse(tokenStr);
this.userToken = new AuthToken(token.token, new Date(token.expires));
if (!this.userToken || this.userToken.isExpired()) {
this.logout();
this.redirectToAuthenticator();
}
this.getUserInfoFromToken();
}
this.redirectToAuthenticator(requestedUrl);
}

public redirectToAuthenticator() {
const client_id = this.envService.clientId;
const callback = location.origin + this.envService.baseHref + 'callback';
const state = Math.random().toString(36);
// tslint:disable-next-line:max-line-length
const AUTH_URL_V3 = `${this.envService.tapisUrl}/v3/oauth2/authorize?client_id=${client_id}&response_type=token&redirect_uri=${callback}`;

window.location.href = AUTH_URL_V3;
public redirectToAuthenticator(requestedUrl: string) {
const GEOAPI_AUTH_URL = `${this.envService.apiUrl}/auth/login?to=${requestedUrl}`;
window.location.href = GEOAPI_AUTH_URL;
}

/**
Expand All @@ -71,6 +54,19 @@ export class AuthService {
return false;
}

/**
* Checks to see if there is a logged in user but token is expired;
*/
public isLoggedInButTokenExpired(): boolean {
const tokenStr = localStorage.getItem(this.getTokenKeyword());
if (tokenStr) {
const token = JSON.parse(tokenStr);
this.userToken = new AuthToken(token.token, new Date(token.expires));
return this.userToken && this.userToken.isExpired();
}
return false;
}

public logout(): void {
this.userToken = null;
localStorage.removeItem(this.getTokenKeyword());
Expand All @@ -93,11 +89,4 @@ export class AuthService {
const u = new AuthenticatedUser(decodedJwt['tapis/username']);
this._currentUser.next(u);
}

checkLoggedIn(): void {
if (!this.isLoggedIn()) {
this.logout();
this.redirectToAuthenticator();
}
}
}
2 changes: 0 additions & 2 deletions angular/src/app/services/notifications.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export class NotificationsService {
}

getRecent(): void {
this.authService.checkLoggedIn();
const baseUrl = this.envService.apiUrl + '/notifications/';
const now = new Date();
const then = new Date(now.getTime() - this.TIMEOUT);
Expand Down Expand Up @@ -75,7 +74,6 @@ export class NotificationsService {
}

getRecentProgress(): void {
this.authService.checkLoggedIn();
const baseUrl = this.envService.apiUrl + '/notifications/progress';
this.http.get<Array<IProgressNotification>>(baseUrl).subscribe((notes) => {
this._progressNotifications.next(notes);
Expand Down

0 comments on commit adb862b

Please sign in to comment.