@sasjs/adapter 4.10.1 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Provides preferred language of the user.
3
+ * @returns A string representing the preferred language of the user, usually the language of the browser UI. Examples of valid language codes include "en", "en-US", "fr", "fr-FR", "es-ES". More info available https://datatracker.ietf.org/doc/html/rfc5646
4
+ */
5
+ export declare const getUserLanguage: () => string | undefined;
@@ -20,3 +20,4 @@ export * from './serialize';
20
20
  export * from './splitChunks';
21
21
  export * from './validateInput';
22
22
  export * from './getFormData';
23
+ export * from './getUserLanguage';
package/package.json CHANGED
@@ -84,5 +84,5 @@
84
84
  "https": "1.0.0",
85
85
  "tough-cookie": "4.1.3"
86
86
  },
87
- "version": "4.10.1"
87
+ "version": "4.11.0"
88
88
  }
@@ -7,6 +7,7 @@ import { extractUserLongNameSas9 } from '../utils/sas9/extractUserLongNameSas9'
7
7
  import { openWebPage } from './openWebPage'
8
8
  import { verifySas9Login } from './verifySas9Login'
9
9
  import { verifySasViyaLogin } from './verifySasViyaLogin'
10
+ import { isLogInSuccessHeaderPresent } from './'
10
11
 
11
12
  export class AuthManager {
12
13
  public userName = ''
@@ -14,6 +15,7 @@ export class AuthManager {
14
15
  private loginUrl: string
15
16
  private logoutUrl: string
16
17
  private redirectedLoginUrl = `/SASLogon` //SAS 9 M8 no longer redirects from `/SASLogon/home` to the login page. `/SASLogon` seems to be stable enough across SAS versions
18
+
17
19
  constructor(
18
20
  private serverUrl: string,
19
21
  private serverType: ServerType,
@@ -27,6 +29,8 @@ export class AuthManager {
27
29
  : this.serverType === ServerType.SasViya
28
30
  ? '/SASLogon/logout.do?'
29
31
  : '/SASLogon/logout'
32
+
33
+ this.redirectedLoginUrl = this.serverUrl + this.redirectedLoginUrl
30
34
  }
31
35
 
32
36
  /**
@@ -129,7 +133,7 @@ export class AuthManager {
129
133
 
130
134
  let loginResponse = await this.sendLoginRequest(loginForm, loginParams)
131
135
 
132
- let isLoggedIn = isLogInSuccess(this.serverType, loginResponse)
136
+ let isLoggedIn = isLogInSuccessHeaderPresent(this.serverType, loginResponse)
133
137
 
134
138
  if (!isLoggedIn) {
135
139
  if (isCredentialsVerifyError(loginResponse)) {
@@ -214,7 +218,7 @@ export class AuthManager {
214
218
  * - a boolean `isLoggedIn`
215
219
  * - a string `userName`,
216
220
  * - a string `userFullName` and
217
- * - a form `loginForm` if not loggedin.
221
+ * - a form `loginForm` if not loggedIn.
218
222
  */
219
223
  public async checkSession(): Promise<LoginResultInternal> {
220
224
  const { isLoggedIn, userName, userLongName } = await this.fetchUserName()
@@ -381,9 +385,3 @@ const isCredentialsVerifyError = (response: string): boolean =>
381
385
  /An error occurred while the system was verifying your credentials. Please enter your credentials again./gm.test(
382
386
  response
383
387
  )
384
-
385
- const isLogInSuccess = (serverType: ServerType, response: any): boolean => {
386
- if (serverType === ServerType.Sasjs) return response?.loggedin
387
-
388
- return /You have signed in/gm.test(response)
389
- }
package/src/auth/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './AuthManager'
2
2
  export * from './isAuthorizeFormRequired'
3
3
  export * from './isLoginRequired'
4
+ export * from './loginHeader'
@@ -0,0 +1,97 @@
1
+ import { ServerType } from '@sasjs/utils/types'
2
+ import { getUserLanguage } from '../utils'
3
+
4
+ const enLoginSuccessHeader = 'You have signed in.'
5
+
6
+ export const defaultSuccessHeaderKey = 'default'
7
+
8
+ // The following headers provided by https://github.com/sasjs/adapter/issues/835#issuecomment-2177818601
9
+ export const loginSuccessHeaders: { [key: string]: string } = {
10
+ es: `Ya se ha iniciado la sesi\u00f3n.`,
11
+ th: `\u0e04\u0e38\u0e13\u0e25\u0e07\u0e0a\u0e37\u0e48\u0e2d\u0e40\u0e02\u0e49\u0e32\u0e43\u0e0a\u0e49\u0e41\u0e25\u0e49\u0e27`,
12
+ ja: `\u30b5\u30a4\u30f3\u30a4\u30f3\u3057\u307e\u3057\u305f\u3002`,
13
+ nb: `Du har logget deg p\u00e5.`,
14
+ sl: `Prijavili ste se.`,
15
+ ar: `\u0644\u0642\u062f \u0642\u0645\u062a `,
16
+ sk: `Prihl\u00e1sili ste sa.`,
17
+ zh_HK: `\u60a8\u5df2\u767b\u5165\u3002`,
18
+ zh_CN: `\u60a8\u5df2\u767b\u5f55\u3002`,
19
+ it: `L'utente si \u00e8 connesso.`,
20
+ sv: `Du har loggat in.`,
21
+ he: `\u05e0\u05db\u05e0\u05e1\u05ea `,
22
+ nl: `U hebt zich aangemeld.`,
23
+ pl: `Zosta\u0142e\u015b zalogowany.`,
24
+ ko: `\ub85c\uadf8\uc778\ud588\uc2b5\ub2c8\ub2e4.`,
25
+ zh_TW: `\u60a8\u5df2\u767b\u5165\u3002`,
26
+ tr: `Oturum a\u00e7t\u0131n\u0131z.`,
27
+ iw: `\u05e0\u05db\u05e0\u05e1\u05ea `,
28
+ fr: `Vous \u00eates connect\u00e9.`,
29
+ uk: `\u0412\u0438 \u0432\u0432\u0456\u0439\u0448\u043b\u0438 \u0432 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441.`,
30
+ pt_BR: `Voc\u00ea se conectou.`,
31
+ no: `Du har logget deg p\u00e5.`,
32
+ cs: `Jste p\u0159ihl\u00e1\u0161eni.`,
33
+ fi: `Olet kirjautunut sis\u00e4\u00e4n.`,
34
+ ru: `\u0412\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438 \u0432\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443.`,
35
+ el: `\u0388\u03c7\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af.`,
36
+ hr: `Prijavili ste se.`,
37
+ da: `Du er logget p\u00e5.`,
38
+ de: `Sie sind jetzt angemeldet.`,
39
+ sh: `Prijavljeni ste.`,
40
+ pt: `Iniciou sess\u00e3o.`,
41
+ hu: `Bejelentkezett.`,
42
+ sr: `Prijavljeni ste.`,
43
+ en: enLoginSuccessHeader,
44
+ [defaultSuccessHeaderKey]: enLoginSuccessHeader
45
+ }
46
+
47
+ /**
48
+ * Provides expected login header based on language settings of the browser.
49
+ * @returns - expected header as a string.
50
+ */
51
+ export const getExpectedLogInSuccessHeader = (): string => {
52
+ // get default success header
53
+ let successHeader = loginSuccessHeaders[defaultSuccessHeaderKey]
54
+
55
+ // get user language based on language settings of the browser
56
+ const userLang = getUserLanguage()
57
+
58
+ if (userLang) {
59
+ // get success header on exact match of the language code
60
+ let userLangSuccessHeader = loginSuccessHeaders[userLang]
61
+
62
+ // handle case when there is no exact match of the language code
63
+ if (!userLangSuccessHeader) {
64
+ // get all supported language codes
65
+ const headerLanguages = Object.keys(loginSuccessHeaders)
66
+
67
+ // find language code on partial match
68
+ const headerLanguage = headerLanguages.find((language) =>
69
+ new RegExp(language, 'i').test(userLang)
70
+ )
71
+
72
+ // reassign success header if partial match was found
73
+ if (headerLanguage) {
74
+ successHeader = loginSuccessHeaders[headerLanguage]
75
+ }
76
+ } else {
77
+ successHeader = userLangSuccessHeader
78
+ }
79
+ }
80
+
81
+ return successHeader
82
+ }
83
+
84
+ /**
85
+ * Checks if Login success header is present in the response based on language settings of the browser.
86
+ * @param serverType - server type.
87
+ * @param response - response object.
88
+ * @returns - boolean indicating if Login success header is present.
89
+ */
90
+ export const isLogInSuccessHeaderPresent = (
91
+ serverType: ServerType,
92
+ response: any
93
+ ): boolean => {
94
+ if (serverType === ServerType.Sasjs) return response?.loggedIn
95
+
96
+ return new RegExp(getExpectedLogInSuccessHeader(), 'gm').test(response)
97
+ }
@@ -1,7 +1,6 @@
1
1
  import { SasAuthResponse } from '@sasjs/utils/types'
2
2
 
3
3
  export const mockLoginAuthoriseRequiredResponse = `<form id="application_authorization" action="/SASLogon/oauth/authorize" method="POST"><input type="hidden" name="X-Uaa-Csrf" value="2nfuxIn6WaOURWL7tzTXCe"/>`
4
- export const mockLoginSuccessResponse = `You have signed in`
5
4
 
6
5
  export const mockAuthResponse: SasAuthResponse = {
7
6
  access_token: 'acc355',
@@ -1,4 +1,5 @@
1
1
  import { delay } from '../utils'
2
+ import { getExpectedLogInSuccessHeader } from './'
2
3
 
3
4
  export async function verifySas9Login(loginPopup: Window): Promise<{
4
5
  isLoggedIn: boolean
@@ -6,13 +7,17 @@ export async function verifySas9Login(loginPopup: Window): Promise<{
6
7
  let isLoggedIn = false
7
8
  let startTime = new Date()
8
9
  let elapsedSeconds = 0
10
+
9
11
  do {
10
12
  await delay(1000)
11
13
  if (loginPopup.closed) break
12
14
 
13
15
  isLoggedIn =
14
16
  loginPopup.window.location.href.includes('SASLogon') &&
15
- loginPopup.window.document.body.innerText.includes('You have signed in.')
17
+ loginPopup.window.document.body.innerText.includes(
18
+ getExpectedLogInSuccessHeader()
19
+ )
20
+
16
21
  elapsedSeconds = (new Date().valueOf() - startTime.valueOf()) / 1000
17
22
  } while (!isLoggedIn && elapsedSeconds < 5 * 60)
18
23
 
@@ -1,4 +1,5 @@
1
1
  import { delay } from '../utils'
2
+ import { getExpectedLogInSuccessHeader } from './'
2
3
 
3
4
  export async function verifySasViyaLogin(loginPopup: Window): Promise<{
4
5
  isLoggedIn: boolean
@@ -6,23 +7,32 @@ export async function verifySasViyaLogin(loginPopup: Window): Promise<{
6
7
  let isLoggedIn = false
7
8
  let startTime = new Date()
8
9
  let elapsedSeconds = 0
10
+
9
11
  do {
10
12
  await delay(1000)
13
+
11
14
  if (loginPopup.closed) break
15
+
12
16
  isLoggedIn = isLoggedInSASVIYA()
17
+
13
18
  elapsedSeconds = (new Date().valueOf() - startTime.valueOf()) / 1000
14
19
  } while (!isLoggedIn && elapsedSeconds < 5 * 60)
15
20
 
16
21
  let isAuthorized = false
22
+
17
23
  startTime = new Date()
24
+
18
25
  do {
19
26
  await delay(1000)
27
+
20
28
  if (loginPopup.closed) break
29
+
21
30
  isAuthorized =
22
31
  loginPopup.window.location.href.includes('SASLogon') ||
23
32
  loginPopup.window.document.body?.innerText?.includes(
24
- 'You have signed in.'
33
+ getExpectedLogInSuccessHeader()
25
34
  )
35
+
26
36
  elapsedSeconds = (new Date().valueOf() - startTime.valueOf()) / 1000
27
37
  } while (!isAuthorized && elapsedSeconds < 5 * 60)
28
38
 
@@ -0,0 +1,10 @@
1
+ interface IEnavigator {
2
+ userLanguage?: string
3
+ }
4
+
5
+ /**
6
+ * Provides preferred language of the user.
7
+ * @returns A string representing the preferred language of the user, usually the language of the browser UI. Examples of valid language codes include "en", "en-US", "fr", "fr-FR", "es-ES". More info available https://datatracker.ietf.org/doc/html/rfc5646
8
+ */
9
+ export const getUserLanguage = () =>
10
+ window.navigator.language || (window.navigator as IEnavigator).userLanguage
@@ -20,3 +20,4 @@ export * from './serialize'
20
20
  export * from './splitChunks'
21
21
  export * from './validateInput'
22
22
  export * from './getFormData'
23
+ export * from './getUserLanguage'
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Provides preferred language of the user.
3
+ * @returns A string representing the preferred language of the user, usually the language of the browser UI. Examples of valid language codes include "en", "en-US", "fr", "fr-FR", "es-ES". More info available https://datatracker.ietf.org/doc/html/rfc5646
4
+ */
5
+ export declare const getUserLanguage: () => string | undefined;
package/utils/index.d.ts CHANGED
@@ -20,3 +20,4 @@ export * from './serialize';
20
20
  export * from './splitChunks';
21
21
  export * from './validateInput';
22
22
  export * from './getFormData';
23
+ export * from './getUserLanguage';