diff --git a/src/SASjs.ts b/src/SASjs.ts
index 1d3281a..77f4680 100644
--- a/src/SASjs.ts
+++ b/src/SASjs.ts
@@ -11,12 +11,8 @@ require('isomorphic-fetch')
import {
convertToCSV,
compareTimestamps,
- serialize,
- isAuthorizeFormRequired,
- parseAndSubmitAuthorizeForm,
splitChunks,
isLogInRequired,
- isLogInSuccess,
parseSourceCode,
parseGeneratedCode,
parseWeboutResponse,
@@ -38,6 +34,7 @@ import {
import { SASViyaApiClient } from './SASViyaApiClient'
import { SAS9ApiClient } from './SAS9ApiClient'
import { FileUploader } from './FileUploader'
+import { AuthManager } from './auth/auth'
const defaultConfig: SASjsConfig = {
serverUrl: '',
@@ -59,8 +56,6 @@ const requestRetryLimit = 5
export default class SASjs {
private sasjsConfig: SASjsConfig = new SASjsConfig()
private jobsPath: string = ''
- private logoutUrl: string = ''
- private loginUrl: string = ''
private csrfTokenApi: CsrfToken | null = null
private csrfTokenWeb: CsrfToken | null = null
private retryCountWeb: number = 0
@@ -68,10 +63,10 @@ export default class SASjs {
private retryCountJeseApi: number = 0
private sasjsRequests: SASjsRequest[] = []
private sasjsWaitingRequests: SASjsWaitingRequest[] = []
- private userName: string = ''
private sasViyaApiClient: SASViyaApiClient | null = null
private sas9ApiClient: SAS9ApiClient | null = null
private fileUploader: FileUploader | null = null
+ private authManager: AuthManager | null = null
constructor(config?: any) {
this.sasjsConfig = {
@@ -433,7 +428,7 @@ export default class SASjs {
*
*/
public getUserName() {
- return this.userName
+ return this.authManager!.userName
}
/**
@@ -475,48 +470,12 @@ export default class SASjs {
}
}
- private async getLoginForm(response: any) {
- const pattern: RegExp = /
/
- const matches = pattern.exec(response)
- const formInputs: any = {}
-
- if (matches && matches.length) {
- this.setLoginUrl(matches)
- const inputs = response.match(/]*>/g)
-
- if (inputs) {
- inputs.forEach((inputStr: string) => {
- const valueMatch = inputStr.match(/name="([^"]*)"\svalue="([^"]*)/)
-
- if (valueMatch && valueMatch.length) {
- formInputs[valueMatch[1]] = valueMatch[2]
- }
- })
- }
- }
-
- return Object.keys(formInputs).length ? formInputs : null
- }
-
/**
* Checks whether a session is active, or login is required.
* @returns - a promise which resolves with an object containing two values - a boolean `isLoggedIn`, and a string `userName`.
*/
public async checkSession() {
- const loginResponse = await fetch(this.loginUrl.replace('.do', ''))
- const responseText = await loginResponse.text()
- const isLoggedIn = / response.text())
- .then(async (responseText) => {
- let authFormRes: any
- let loggedIn
-
- if (isAuthorizeFormRequired(responseText)) {
- authFormRes = await parseAndSubmitAuthorizeForm(
- responseText,
- this.sasjsConfig.serverUrl
- )
- } else {
- loggedIn = isLogInSuccess(responseText)
- }
-
- if (!loggedIn) {
- const currentSession = await this.checkSession()
- loggedIn = currentSession.isLoggedIn
- }
-
- if (loggedIn) {
- this.resendWaitingRequests()
- }
-
- return {
- isLoggedIn: loggedIn,
- userName: this.userName
- }
- })
- .catch((e) => Promise.reject(e))
+ return this.authManager!.logIn(username, password)
}
/**
* Logs out of the configured SAS server.
*/
public logOut() {
- return new Promise((resolve, reject) => {
- const logOutURL = `${this.sasjsConfig.serverUrl}${this.logoutUrl}`
- fetch(logOutURL)
- .then(() => {
- resolve(true)
- })
- .catch((err: Error) => reject(err))
- })
+ return this.authManager!.logOut()
}
/**
@@ -1174,7 +1066,6 @@ export default class SASjs {
this.sasjsWaitingRequests.push(sasjsWaitingRequest)
} else {
if (config.serverType === ServerType.SAS9 && config.debug) {
- this.updateUsername(responseText)
const jsonResponseText = parseWeboutResponse(responseText)
if (jsonResponseText !== '') {
@@ -1194,7 +1085,6 @@ export default class SASjs {
try {
this.parseSASVIYADebugResponse(responseText).then(
(resText: any) => {
- this.updateUsername(resText)
try {
resolve(JSON.parse(resText))
} catch (e) {
@@ -1224,7 +1114,6 @@ export default class SASjs {
)
}
} else {
- this.updateUsername(responseText)
if (
responseText.includes(
'The requested URL /SASStoredProcess/do/ was not found on this server.'
@@ -1304,19 +1193,6 @@ export default class SASjs {
return requestParams
}
- private updateUsername(response: any) {
- try {
- const responseJson = JSON.parse(response)
- if (this.sasjsConfig.serverType === ServerType.SAS9) {
- this.userName = responseJson['_METAUSER']
- } else {
- this.userName = responseJson['SYSUSERID']
- }
- } catch (e) {
- this.userName = ''
- }
- }
-
private parseSASVIYADebugResponse(response: string) {
return new Promise((resolve, reject) => {
const iframeStart = response.split(
@@ -1527,11 +1403,11 @@ export default class SASjs {
this.sasjsConfig.serverType === ServerType.SASViya
? this.sasjsConfig.pathSASViya
: this.sasjsConfig.pathSAS9
- this.loginUrl = `${this.sasjsConfig.serverUrl}/SASLogon/login`
- this.logoutUrl =
- this.sasjsConfig.serverType === ServerType.SAS9
- ? '/SASLogon/logout?'
- : '/SASLogon/logout.do?'
+ this.authManager = new AuthManager(
+ this.sasjsConfig.serverUrl,
+ this.sasjsConfig.serverType!,
+ this.resendWaitingRequests
+ )
if (this.sasjsConfig.serverType === ServerType.SASViya) {
if (this.sasViyaApiClient)
@@ -1563,24 +1439,6 @@ export default class SASjs {
)
}
- private setLoginUrl = (matches: RegExpExecArray) => {
- let parsedURL = matches[1].replace(/\?.*/, '')
- if (parsedURL[0] === '/') {
- parsedURL = parsedURL.substr(1)
-
- const tempLoginLink = this.sasjsConfig.serverUrl
- ? `${this.sasjsConfig.serverUrl}/${parsedURL}`
- : `${parsedURL}`
-
- const loginUrl = tempLoginLink
-
- this.loginUrl =
- this.sasjsConfig.serverType === ServerType.SASViya
- ? tempLoginLink
- : loginUrl.replace('.do', '')
- }
- }
-
private async createFoldersAndServices(
parentFolder: string,
membersJson: any[],
diff --git a/src/auth/auth.ts b/src/auth/auth.ts
new file mode 100644
index 0000000..74945fa
--- /dev/null
+++ b/src/auth/auth.ts
@@ -0,0 +1,205 @@
+import axios, { AxiosInstance } from 'axios'
+import { ServerType } from '../types'
+import {
+ serialize,
+ isAuthorizeFormRequired,
+ parseAndSubmitAuthorizeForm,
+ isLogInSuccess
+} from '../utils'
+
+export class AuthManager {
+ public userName = ''
+ private loginUrl: string
+ private logoutUrl: string
+ private httpClient: AxiosInstance
+ constructor(
+ private serverUrl: string,
+ private serverType: ServerType,
+ private loginCallback: Function
+ ) {
+ this.httpClient = axios.create({ baseURL: this.serverUrl })
+ this.loginUrl = `/SASLogon/login`
+ this.logoutUrl =
+ this.serverType === ServerType.SAS9
+ ? '/SASLogon/logout?'
+ : '/SASLogon/logout.do?'
+ }
+
+ /**
+ * Logs into the SAS server with the supplied credentials.
+ * @param username - a string representing the username.
+ * @param password - a string representing the password.
+ */
+ public async logIn(username: string, password: string) {
+ const loginParams: any = {
+ _service: 'default',
+ username,
+ password
+ }
+
+ this.userName = loginParams.username
+
+ const { isLoggedIn, loginForm } = await this.checkSession()
+ if (isLoggedIn) {
+ this.loginCallback()
+
+ return {
+ isLoggedIn,
+ userName: this.userName
+ }
+ }
+
+ for (const key in loginForm) {
+ loginParams[key] = loginForm[key]
+ }
+ const loginParamsStr = serialize(loginParams)
+
+ const loginResponse = await axios
+ .post(this.loginUrl, loginParamsStr, {
+ withCredentials: true,
+ responseType: 'text',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+ })
+ .then((response) => response.data)
+
+ let loggedIn
+
+ if (isAuthorizeFormRequired(loginResponse)) {
+ await parseAndSubmitAuthorizeForm(loginResponse, this.serverUrl)
+ } else {
+ loggedIn = isLogInSuccess(loginResponse)
+ }
+
+ if (!loggedIn) {
+ const currentSession = await this.checkSession()
+ loggedIn = currentSession.isLoggedIn
+ }
+
+ if (loggedIn) {
+ this.loginCallback()
+ }
+
+ return {
+ isLoggedIn: !!loggedIn,
+ userName: this.userName
+ }
+
+ return {
+ isLoggedIn: isLogInSuccess(loginResponse),
+ userName: this.userName
+ }
+
+ return fetch(this.loginUrl, {
+ method: 'POST',
+ credentials: 'include',
+ referrerPolicy: 'same-origin',
+ body: loginParamsStr,
+ headers: new Headers({
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ })
+ })
+ .then((response) => response.text())
+ .then(async (responseText) => {
+ let loggedIn
+
+ if (isAuthorizeFormRequired(responseText)) {
+ const authFormResponse = await parseAndSubmitAuthorizeForm(
+ responseText,
+ this.serverUrl
+ )
+ } else {
+ loggedIn = isLogInSuccess(responseText)
+ }
+
+ if (!loggedIn) {
+ const currentSession = await this.checkSession()
+ loggedIn = currentSession.isLoggedIn
+ }
+
+ if (loggedIn) {
+ this.loginCallback()
+ }
+
+ return {
+ isLoggedIn: loggedIn,
+ userName: this.userName
+ }
+ })
+ .catch((e) => Promise.reject(e))
+ }
+
+ /**
+ * Checks whether a session is active, or login is required.
+ * @returns - a promise which resolves with an object containing two values - a boolean `isLoggedIn`, and a string `userName`.
+ */
+ public async checkSession() {
+ const loginResponse = await fetch(this.loginUrl.replace('.do', ''))
+ const responseText = await loginResponse.text()
+ const isLoggedIn = //
+ const matches = pattern.exec(response)
+ const formInputs: any = {}
+
+ if (matches && matches.length) {
+ this.setLoginUrl(matches)
+ const inputs = response.match(/]*>/g)
+
+ if (inputs) {
+ inputs.forEach((inputStr: string) => {
+ const valueMatch = inputStr.match(/name="([^"]*)"\svalue="([^"]*)/)
+
+ if (valueMatch && valueMatch.length) {
+ formInputs[valueMatch[1]] = valueMatch[2]
+ }
+ })
+ }
+ }
+
+ return Object.keys(formInputs).length ? formInputs : null
+ }
+
+ private setLoginUrl = (matches: RegExpExecArray) => {
+ let parsedURL = matches[1].replace(/\?.*/, '')
+ if (parsedURL[0] === '/') {
+ parsedURL = parsedURL.substr(1)
+
+ const tempLoginLink = this.serverUrl
+ ? `${this.serverUrl}/${parsedURL}`
+ : `${parsedURL}`
+
+ const loginUrl = tempLoginLink
+
+ this.loginUrl =
+ this.serverType === ServerType.SASViya
+ ? tempLoginLink
+ : loginUrl.replace('.do', '')
+ }
+ }
+
+ /**
+ * Logs out of the configured SAS server.
+ */
+ public logOut() {
+ return new Promise((resolve, reject) => {
+ fetch(this.logoutUrl)
+ .then(() => {
+ resolve(true)
+ })
+ .catch((err: Error) => reject(err))
+ })
+ }
+}