diff --git a/src/auth/AuthManager.ts b/src/auth/AuthManager.ts index ea9b9ba8..21a72998 100644 --- a/src/auth/AuthManager.ts +++ b/src/auth/AuthManager.ts @@ -78,7 +78,16 @@ export class AuthManager { if (isLoggedIn) { if (this.serverType === ServerType.Sas9) { - await this.performCASSecurityCheck() + const casSecurityCheckResponse = await this.performCASSecurityCheck() + + if (isPublicAccessDenied(casSecurityCheckResponse.result)) { + return { + isLoggedIn: false, + userName: this.userName || '', + userLongName: this.userLongName || '', + errorMessage: 'Public access has been denied.' + } + } } const { userName, userLongName } = await this.fetchUserName() @@ -149,7 +158,17 @@ export class AuthManager { if (isLoggedIn) { if (this.serverType === ServerType.Sas9) { - await this.performCASSecurityCheck() + const casSecurityCheckResponse = await this.performCASSecurityCheck() + if (isPublicAccessDenied(casSecurityCheckResponse.result)) { + isLoggedIn = false + + return { + isLoggedIn, + userName: this.userName || '', + userLongName: this.userLongName || '', + errorMessage: 'Public access has been denied.' + } + } } this.loginCallback() @@ -166,11 +185,15 @@ export class AuthManager { private async performCASSecurityCheck() { const casAuthenticationUrl = `${this.serverUrl}/SASStoredProcess/j_spring_cas_security_check` - await this.requestClient + return await this.requestClient .get(`/SASLogon/login?service=${casAuthenticationUrl}`, undefined) .catch((err) => { // ignore if resource not found error if (!(err instanceof NotFoundError)) throw err + + return { + result: '' + } }) } @@ -387,3 +410,7 @@ const isLogInSuccess = (serverType: ServerType, response: any): boolean => { return /You have signed in/gm.test(response) } + +const isPublicAccessDenied = (response: any): boolean => { + return /Public access has been denied/gm.test(response) +} diff --git a/src/auth/spec/AuthManager.spec.ts b/src/auth/spec/AuthManager.spec.ts index 1eb7392c..d481a957 100644 --- a/src/auth/spec/AuthManager.spec.ts +++ b/src/auth/spec/AuthManager.spec.ts @@ -5,6 +5,7 @@ import axios from 'axios' import { mockedCurrentUserApi, mockLoginAuthoriseRequiredResponse, + mockLoginPublicAccessDeniedResponse, mockLoginSuccessResponse } from './mockResponses' import { serialize } from '../../utils' @@ -213,6 +214,61 @@ describe('AuthManager', () => { expect(authCallback).toHaveBeenCalledTimes(1) }) + it('should post a login & a cas_security request to the SAS9 server when not logged in & get rejected due to public access denied', async () => { + const serverType = ServerType.Sas9 + const authManager = new AuthManager( + serverUrl, + serverType, + requestClient, + authCallback + ) + jest.spyOn(authManager, 'checkSession').mockImplementation(() => + Promise.resolve({ + isLoggedIn: false, + userName: '', + userLongName: '', + loginForm: { name: 'test' } + }) + ) + mockedAxios.post.mockImplementationOnce(() => + Promise.resolve({ data: mockLoginSuccessResponse }) + ) + mockedAxios.get.mockImplementationOnce(() => + Promise.resolve({ data: mockLoginPublicAccessDeniedResponse }) + ) + + const loginResponse = await authManager.logIn(userName, password) + + expect(loginResponse.isLoggedIn).toBeFalse() + expect(loginResponse.userName).toEqual('') + expect(loginResponse.errorMessage).toEqual( + 'Public access has been denied.' + ) + + const loginParams = serialize({ + _service: 'default', + username: userName, + password, + name: 'test' + }) + expect(mockedAxios.post).toHaveBeenCalledWith( + `/SASLogon/login`, + loginParams, + { + withCredentials: true, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: '*/*' + } + } + ) + const casAuthenticationUrl = `${serverUrl}/SASStoredProcess/j_spring_cas_security_check` + expect(mockedAxios.get).toHaveBeenCalledWith( + `/SASLogon/login?service=${casAuthenticationUrl}`, + getHeadersJson + ) + }) + it('should return empty username if unable to logged in', async () => { const authManager = new AuthManager( serverUrl, @@ -422,6 +478,53 @@ describe('AuthManager', () => { expect(authCallback).toHaveBeenCalledTimes(1) }) + it('should return error if public account access is denied', async () => { + const serverType = ServerType.Sas9 + const authManager = new AuthManager( + serverUrl, + serverType, + requestClient, + authCallback + ) + jest + .spyOn(authManager, 'fetchUserName') + .mockImplementationOnce(() => + Promise.resolve({ + isLoggedIn: false, + userName: '' + }) + ) + .mockImplementationOnce(() => + Promise.resolve({ + isLoggedIn: true, + userName + }) + ) + mockedAxios.get.mockImplementation(() => + Promise.resolve({ data: mockLoginPublicAccessDeniedResponse }) + ) + + const loginResponse = await authManager.redirectedLogIn({}) + + expect(loginResponse.isLoggedIn).toBeFalse() + expect(loginResponse.userName).toEqual('') + expect(loginResponse.errorMessage).toEqual( + 'Public access has been denied.' + ) + + expect(openWebPageModule.openWebPage).toHaveBeenCalledWith( + `/SASLogon`, + 'SASLogon', + { + width: 500, + height: 600 + }, + undefined + ) + expect(authManager['fetchUserName']).toHaveBeenCalledTimes(1) + expect(verifySas9LoginModule.verifySas9Login).toHaveBeenCalledTimes(1) + }) + it('should return empty username if user unable to re-login via pop up', async () => { const authManager = new AuthManager( serverUrl, diff --git a/src/auth/spec/mockResponses.ts b/src/auth/spec/mockResponses.ts index 4aaad1d4..c032f9be 100644 --- a/src/auth/spec/mockResponses.ts +++ b/src/auth/spec/mockResponses.ts @@ -2,6 +2,7 @@ import { SasAuthResponse } from '@sasjs/utils/types' export const mockLoginAuthoriseRequiredResponse = `
` export const mockLoginSuccessResponse = `You have signed in` +export const mockLoginPublicAccessDeniedResponse = `Public access has been denied` export const mockAuthResponse: SasAuthResponse = { access_token: 'acc355', diff --git a/src/job-execution/WebJobExecutor.ts b/src/job-execution/WebJobExecutor.ts index 2fd65e59..2da6b36a 100644 --- a/src/job-execution/WebJobExecutor.ts +++ b/src/job-execution/WebJobExecutor.ts @@ -187,6 +187,12 @@ export class WebJobExecutor extends BaseJobExecutor { { result: jsonResponse, log: res.log }, extraResponseAttributes ) + + if (this.isPublicAccessDenied(jsonResponse)) + reject( + new ErrorResponse('Public access has been denied', responseObject) + ) + resolve(responseObject) }) .catch(async (e: Error) => { @@ -262,4 +268,8 @@ export class WebJobExecutor extends BaseJobExecutor { } return uri } + + private isPublicAccessDenied = (response: string): boolean => { + return /Public access has been denied/gm.test(response) + } } diff --git a/src/types/Login.ts b/src/types/Login.ts index e29a2d2d..479d55eb 100644 --- a/src/types/Login.ts +++ b/src/types/Login.ts @@ -6,6 +6,7 @@ export interface LoginResult { isLoggedIn: boolean userName: string userLongName: string + errorMessage?: string } export interface LoginResultInternal { isLoggedIn: boolean