mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
056a436e10 | ||
|
|
06d59c618c | ||
|
|
a0e7875ae6 | ||
|
|
24966e695a | ||
|
|
5c40d8a342 | ||
| 6f5566dabb | |||
| d93470d183 | |||
| 330c020933 | |||
|
|
a810f6c7cf | ||
|
|
5d6c6086b4 | ||
|
|
0edcbdcefc | ||
|
|
ea0222f218 | ||
| edc2e2a302 | |||
|
|
efd2e1450e | ||
|
|
1092a73c10 | ||
| 9977c9d161 | |||
|
|
5c0eff5197 | ||
|
|
3bda991a58 | ||
| 0327f7c6ec | |||
| 92549402eb | |||
|
|
b88c911527 | ||
|
|
8b12f31060 | ||
|
|
e65cba9af0 | ||
| 0749d65173 | |||
| 06d3b17154 |
36
CHANGELOG.md
36
CHANGELOG.md
@@ -1,3 +1,39 @@
|
|||||||
|
## [0.21.4](https://github.com/sasjs/server/compare/v0.21.3...v0.21.4) (2022-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* removing single quotes from _program value ([a0e7875](https://github.com/sasjs/server/commit/a0e7875ae61cbb6e7d3995d2e36e7300b0daec86))
|
||||||
|
|
||||||
|
## [0.21.3](https://github.com/sasjs/server/compare/v0.21.2...v0.21.3) (2022-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* return same tokens if not expired ([330c020](https://github.com/sasjs/server/commit/330c020933f1080261b38f07d6b627f6d7c62446))
|
||||||
|
|
||||||
|
## [0.21.2](https://github.com/sasjs/server/compare/v0.21.1...v0.21.2) (2022-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* default content-type for sas programs should be text/plain ([9977c9d](https://github.com/sasjs/server/commit/9977c9d161947b11d45ab2513f99a5320a3f5a06))
|
||||||
|
* **studio:** inject program path to code before sending for execution ([edc2e2a](https://github.com/sasjs/server/commit/edc2e2a302ccea4985f3d6b83ef8c23620ab82b6))
|
||||||
|
|
||||||
|
## [0.21.1](https://github.com/sasjs/server/compare/v0.21.0...v0.21.1) (2022-09-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* SASJS_WEBOUT_HEADERS path for windows ([0749d65](https://github.com/sasjs/server/commit/0749d65173e8cfe9a93464711b7be1e123c289ff))
|
||||||
|
|
||||||
|
# [0.21.0](https://github.com/sasjs/server/compare/v0.20.0...v0.21.0) (2022-09-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* sas9 mocker improved - public access denied scenario ([06d3b17](https://github.com/sasjs/server/commit/06d3b1715432ea245ee755ae1dfd0579d3eb30e9))
|
||||||
|
|
||||||
# [0.20.0](https://github.com/sasjs/server/compare/v0.19.0...v0.20.0) (2022-09-16)
|
# [0.20.0](https://github.com/sasjs/server/compare/v0.19.0...v0.20.0) (2022-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
api/mocks/generic/sas9/public-access-denied
Normal file
1
api/mocks/generic/sas9/public-access-denied
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Public access has been denied.
|
||||||
14
api/package-lock.json
generated
14
api/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/core": "^4.31.3",
|
"@sasjs/core": "^4.31.3",
|
||||||
"@sasjs/utils": "2.42.1",
|
"@sasjs/utils": "2.48.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
@@ -1396,9 +1396,9 @@
|
|||||||
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/utils": {
|
"node_modules/@sasjs/utils": {
|
||||||
"version": "2.42.1",
|
"version": "2.48.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.42.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.48.1.tgz",
|
||||||
"integrity": "sha512-DzHNYjeoj2eUkwV7Sa4eHCKRoTrYaQ6eyv6c1U5qOYXwVdZpMoYA3HFsHj55UcMOn2U3CXI5nrR7PZlUmVwVbQ==",
|
"integrity": "sha512-Eu9p66JKLeTj0KK3kfY7YLQYq+MDMS1Q1/FOFfRe9hV23mFsuzierVMrnEYGK0JaHOogdHLmwzg6iVLDT8Jssg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "9.0.13",
|
||||||
@@ -10974,9 +10974,9 @@
|
|||||||
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
"integrity": "sha512-TpVqWl5bqp3JTQjIg0r4WiQg7Ima5f17eAJILJbdYDdXsnLXlA/Csbb95G7eDPhzWpM3C0NrzKek3yvCMGzXIA=="
|
||||||
},
|
},
|
||||||
"@sasjs/utils": {
|
"@sasjs/utils": {
|
||||||
"version": "2.42.1",
|
"version": "2.48.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.42.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.48.1.tgz",
|
||||||
"integrity": "sha512-DzHNYjeoj2eUkwV7Sa4eHCKRoTrYaQ6eyv6c1U5qOYXwVdZpMoYA3HFsHj55UcMOn2U3CXI5nrR7PZlUmVwVbQ==",
|
"integrity": "sha512-Eu9p66JKLeTj0KK3kfY7YLQYq+MDMS1Q1/FOFfRe9hV23mFsuzierVMrnEYGK0JaHOogdHLmwzg6iVLDT8Jssg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "9.0.13",
|
||||||
"@types/prompts": "2.0.13",
|
"@types/prompts": "2.0.13",
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"author": "4GL Ltd",
|
"author": "4GL Ltd",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/core": "^4.31.3",
|
"@sasjs/core": "^4.31.3",
|
||||||
"@sasjs/utils": "2.42.1",
|
"@sasjs/utils": "2.48.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
|
|||||||
@@ -660,10 +660,10 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: 'Execute SAS code.'
|
description: 'Execute Code on the Specified Runtime'
|
||||||
summary: 'Run SAS Code and returns log'
|
summary: 'Run Code and Return Webout Content and Log'
|
||||||
tags:
|
tags:
|
||||||
- CODE
|
- Code
|
||||||
security:
|
security:
|
||||||
-
|
-
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
@@ -1658,8 +1658,8 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
||||||
summary: 'Execute a Stored Program, returns raw _webout content.'
|
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
||||||
tags:
|
tags:
|
||||||
- STP
|
- STP
|
||||||
security:
|
security:
|
||||||
@@ -1667,7 +1667,7 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'Location of SAS or JS code'
|
description: 'Location of code in SASjs Drive'
|
||||||
in: query
|
in: query
|
||||||
name: _program
|
name: _program
|
||||||
required: true
|
required: true
|
||||||
@@ -1685,8 +1685,8 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms\n\nThe response will be a JSON object with the following root attributes:\nlog, webout, headers.\n\nThe webout attribute will be nested JSON ONLY if the response-header\ncontains a content-type of application/json AND it is valid JSON.\nOtherwise it will be a stringified version of the webout content."
|
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
||||||
summary: 'Execute a Stored Program, return a JSON object'
|
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
||||||
tags:
|
tags:
|
||||||
- STP
|
- STP
|
||||||
security:
|
security:
|
||||||
@@ -1694,7 +1694,7 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'Location of SAS or JS code'
|
description: 'Location of code in SASjs Drive'
|
||||||
in: query
|
in: query
|
||||||
name: _program
|
name: _program
|
||||||
required: false
|
required: false
|
||||||
@@ -1798,7 +1798,7 @@ tags:
|
|||||||
name: Client
|
name: Client
|
||||||
description: 'Operations about clients'
|
description: 'Operations about clients'
|
||||||
-
|
-
|
||||||
name: CODE
|
name: Code
|
||||||
description: 'Execution of code (various runtimes are supported)'
|
description: 'Execution of code (various runtimes are supported)'
|
||||||
-
|
-
|
||||||
name: Drive
|
name: Drive
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ export default setProcessVariables().then(async () => {
|
|||||||
app.use(express.json({ limit: '100mb' }))
|
app.use(express.json({ limit: '100mb' }))
|
||||||
app.use(express.static(path.join(__dirname, '../public')))
|
app.use(express.static(path.join(__dirname, '../public')))
|
||||||
|
|
||||||
|
// Body parser is used for decoding the formdata on POST request.
|
||||||
|
// Currently only place we use it is SAS9 Mock - POST /SASLogon/login
|
||||||
|
app.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
await setupFolders()
|
await setupFolders()
|
||||||
await copySASjsCore()
|
await copySASjsCore()
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { InfoJWT } from '../types'
|
|||||||
import {
|
import {
|
||||||
generateAccessToken,
|
generateAccessToken,
|
||||||
generateRefreshToken,
|
generateRefreshToken,
|
||||||
|
getTokensFromDB,
|
||||||
removeTokensInDB,
|
removeTokensInDB,
|
||||||
saveTokensInDB
|
saveTokensInDB
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
@@ -73,6 +74,15 @@ const token = async (data: any): Promise<TokenResponse> => {
|
|||||||
|
|
||||||
AuthController.deleteCode(userInfo.userId, clientId)
|
AuthController.deleteCode(userInfo.userId, clientId)
|
||||||
|
|
||||||
|
// get tokens from DB
|
||||||
|
const existingTokens = await getTokensFromDB(userInfo.userId, clientId)
|
||||||
|
if (existingTokens) {
|
||||||
|
return {
|
||||||
|
accessToken: existingTokens.accessToken,
|
||||||
|
refreshToken: existingTokens.refreshToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const accessToken = generateAccessToken(userInfo)
|
const accessToken = generateAccessToken(userInfo)
|
||||||
const refreshToken = generateRefreshToken(userInfo)
|
const refreshToken = generateRefreshToken(userInfo)
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ interface ExecuteCodePayload {
|
|||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/code')
|
@Route('SASjsApi/code')
|
||||||
@Tags('CODE')
|
@Tags('Code')
|
||||||
export class CodeController {
|
export class CodeController {
|
||||||
/**
|
/**
|
||||||
* Execute SAS code.
|
* Execute Code on the Specified Runtime
|
||||||
* @summary Run SAS Code and returns log
|
* @summary Run Code and Return Webout Content and Log
|
||||||
*/
|
*/
|
||||||
@Post('/execute')
|
@Post('/execute')
|
||||||
public async executeCode(
|
public async executeCode(
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ export class SASSessionController extends SessionController {
|
|||||||
path: sessionFolder
|
path: sessionFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
||||||
|
await createFile(headersPath, 'Content-type: text/plain')
|
||||||
|
|
||||||
// we do not want to leave sessions running forever
|
// we do not want to leave sessions running forever
|
||||||
// we clean them up after a predefined period, if unused
|
// we clean them up after a predefined period, if unused
|
||||||
this.scheduleSessionDestroy(session)
|
this.scheduleSessionDestroy(session)
|
||||||
@@ -170,7 +173,7 @@ ${autoExecContent}`
|
|||||||
session.ready = true
|
session.ready = true
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteSession(session: Session) {
|
private async deleteSession(session: Session) {
|
||||||
// remove the temporary files, to avoid buildup
|
// remove the temporary files, to avoid buildup
|
||||||
await deleteFolder(session.path)
|
await deleteFolder(session.path)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isWindows } from '@sasjs/utils'
|
import { escapeWinSlashes } from '@sasjs/utils'
|
||||||
import { PreProgramVars, Session } from '../../types'
|
import { PreProgramVars, Session } from '../../types'
|
||||||
import { generateFileUploadJSCode } from '../../utils'
|
import { generateFileUploadJSCode } from '../../utils'
|
||||||
import { ExecutionVars } from './'
|
import { ExecutionVars } from './'
|
||||||
@@ -21,13 +21,9 @@ export const createJSProgram = async (
|
|||||||
|
|
||||||
const preProgramVarStatments = `
|
const preProgramVarStatments = `
|
||||||
let _webout = '';
|
let _webout = '';
|
||||||
const weboutPath = '${
|
const weboutPath = '${escapeWinSlashes(weboutPath)}';
|
||||||
isWindows() ? weboutPath.replace(/\\/g, '\\\\') : weboutPath
|
const _SASJS_TOKENFILE = '${escapeWinSlashes(tokenFile)}';
|
||||||
}';
|
const _SASJS_WEBOUT_HEADERS = '${escapeWinSlashes(headersPath)}';
|
||||||
const _SASJS_TOKENFILE = '${
|
|
||||||
isWindows() ? tokenFile.replace(/\\/g, '\\\\') : tokenFile
|
|
||||||
}';
|
|
||||||
const _SASJS_WEBOUT_HEADERS = '${headersPath}';
|
|
||||||
const _SASJS_USERNAME = '${preProgramVariables?.username}';
|
const _SASJS_USERNAME = '${preProgramVariables?.username}';
|
||||||
const _SASJS_USERID = '${preProgramVariables?.userId}';
|
const _SASJS_USERID = '${preProgramVariables?.userId}';
|
||||||
const _SASJS_DISPLAYNAME = '${preProgramVariables?.displayName}';
|
const _SASJS_DISPLAYNAME = '${preProgramVariables?.displayName}';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isWindows } from '@sasjs/utils'
|
import { escapeWinSlashes } from '@sasjs/utils'
|
||||||
import { PreProgramVars, Session } from '../../types'
|
import { PreProgramVars, Session } from '../../types'
|
||||||
import { generateFileUploadPythonCode } from '../../utils'
|
import { generateFileUploadPythonCode } from '../../utils'
|
||||||
import { ExecutionVars } from './'
|
import { ExecutionVars } from './'
|
||||||
@@ -19,14 +19,10 @@ export const createPythonProgram = async (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const preProgramVarStatments = `
|
const preProgramVarStatments = `
|
||||||
_SASJS_SESSION_PATH = '${
|
_SASJS_SESSION_PATH = '${escapeWinSlashes(session.path)}';
|
||||||
isWindows() ? session.path.replace(/\\/g, '\\\\') : session.path
|
_WEBOUT = '${escapeWinSlashes(weboutPath)}';
|
||||||
}';
|
_SASJS_WEBOUT_HEADERS = '${escapeWinSlashes(headersPath)}';
|
||||||
_WEBOUT = '${isWindows() ? weboutPath.replace(/\\/g, '\\\\') : weboutPath}';
|
_SASJS_TOKENFILE = '${escapeWinSlashes(tokenFile)}';
|
||||||
_SASJS_WEBOUT_HEADERS = '${headersPath}';
|
|
||||||
_SASJS_TOKENFILE = '${
|
|
||||||
isWindows() ? tokenFile.replace(/\\/g, '\\\\') : tokenFile
|
|
||||||
}';
|
|
||||||
_SASJS_USERNAME = '${preProgramVariables?.username}';
|
_SASJS_USERNAME = '${preProgramVariables?.username}';
|
||||||
_SASJS_USERID = '${preProgramVariables?.userId}';
|
_SASJS_USERID = '${preProgramVariables?.userId}';
|
||||||
_SASJS_DISPLAYNAME = '${preProgramVariables?.displayName}';
|
_SASJS_DISPLAYNAME = '${preProgramVariables?.displayName}';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isWindows } from '@sasjs/utils'
|
import { escapeWinSlashes } from '@sasjs/utils'
|
||||||
import { PreProgramVars, Session } from '../../types'
|
import { PreProgramVars, Session } from '../../types'
|
||||||
import { generateFileUploadRCode } from '../../utils'
|
import { generateFileUploadRCode } from '../../utils'
|
||||||
import { ExecutionVars } from '.'
|
import { ExecutionVars } from '.'
|
||||||
@@ -19,14 +19,10 @@ export const createRProgram = async (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const preProgramVarStatments = `
|
const preProgramVarStatments = `
|
||||||
._SASJS_SESSION_PATH <- '${
|
._SASJS_SESSION_PATH <- '${escapeWinSlashes(session.path)}';
|
||||||
isWindows() ? session.path.replace(/\\/g, '\\\\') : session.path
|
._WEBOUT <- '${escapeWinSlashes(weboutPath)}';
|
||||||
}';
|
._SASJS_WEBOUT_HEADERS <- '${escapeWinSlashes(headersPath)}';
|
||||||
._WEBOUT <- '${isWindows() ? weboutPath.replace(/\\/g, '\\\\') : weboutPath}';
|
._SASJS_TOKENFILE <- '${escapeWinSlashes(tokenFile)}';
|
||||||
._SASJS_WEBOUT_HEADERS <- '${headersPath}';
|
|
||||||
._SASJS_TOKENFILE <- '${
|
|
||||||
isWindows() ? tokenFile.replace(/\\/g, '\\\\') : tokenFile
|
|
||||||
}';
|
|
||||||
._SASJS_USERNAME <- '${preProgramVariables?.username}';
|
._SASJS_USERNAME <- '${preProgramVariables?.username}';
|
||||||
._SASJS_USERID <- '${preProgramVariables?.userId}';
|
._SASJS_USERID <- '${preProgramVariables?.userId}';
|
||||||
._SASJS_DISPLAYNAME <- '${preProgramVariables?.displayName}';
|
._SASJS_DISPLAYNAME <- '${preProgramVariables?.displayName}';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const createSASProgram = async (
|
|||||||
vars: ExecutionVars,
|
vars: ExecutionVars,
|
||||||
session: Session,
|
session: Session,
|
||||||
weboutPath: string,
|
weboutPath: string,
|
||||||
|
headersPath: string,
|
||||||
tokenFile: string,
|
tokenFile: string,
|
||||||
otherArgs?: any
|
otherArgs?: any
|
||||||
) => {
|
) => {
|
||||||
@@ -23,7 +24,7 @@ export const createSASProgram = async (
|
|||||||
%let _sasjs_displayname=${preProgramVariables?.displayName};
|
%let _sasjs_displayname=${preProgramVariables?.displayName};
|
||||||
%let _sasjs_apiserverurl=${preProgramVariables?.serverUrl};
|
%let _sasjs_apiserverurl=${preProgramVariables?.serverUrl};
|
||||||
%let _sasjs_apipath=/SASjsApi/stp/execute;
|
%let _sasjs_apipath=/SASjsApi/stp/execute;
|
||||||
%let _sasjs_webout_headers=%sysfunc(pathname(work))/../stpsrv_header.txt;
|
%let _sasjs_webout_headers=${headersPath};
|
||||||
%let _metaperson=&_sasjs_displayname;
|
%let _metaperson=&_sasjs_displayname;
|
||||||
%let _metauser=&_sasjs_username;
|
%let _metauser=&_sasjs_username;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export const processProgram = async (
|
|||||||
vars,
|
vars,
|
||||||
session,
|
session,
|
||||||
weboutPath,
|
weboutPath,
|
||||||
|
headersPath,
|
||||||
tokenFile,
|
tokenFile,
|
||||||
otherArgs
|
otherArgs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export interface MockFileRead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MockSas9Controller {
|
export class MockSas9Controller {
|
||||||
private loggedIn: boolean = false
|
private loggedIn: string | undefined
|
||||||
|
|
||||||
@Get('/SASStoredProcess')
|
@Get('/SASStoredProcess')
|
||||||
public async sasStoredProcess(): Promise<Sas9Response> {
|
public async sasStoredProcess(): Promise<Sas9Response> {
|
||||||
@@ -46,6 +46,13 @@ export class MockSas9Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isPublicAccount()) {
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
redirect: '/SASLogon/Login'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let program = req.query._program?.toString() || ''
|
let program = req.query._program?.toString() || ''
|
||||||
program = program.replace('/', '')
|
program = program.replace('/', '')
|
||||||
|
|
||||||
@@ -68,6 +75,23 @@ export class MockSas9Controller {
|
|||||||
|
|
||||||
@Get('/SASLogon/login')
|
@Get('/SASLogon/login')
|
||||||
public async loginGet(): Promise<Sas9Response> {
|
public async loginGet(): Promise<Sas9Response> {
|
||||||
|
if (this.loggedIn) {
|
||||||
|
if (this.isPublicAccount()) {
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
redirect: '/SASStoredProcess/Logoff?publicDenied=true'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return await getMockResponseFromFile([
|
||||||
|
process.cwd(),
|
||||||
|
'mocks',
|
||||||
|
'generic',
|
||||||
|
'sas9',
|
||||||
|
'logged-in'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
'mocks',
|
'mocks',
|
||||||
@@ -78,8 +102,8 @@ export class MockSas9Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('/SASLogon/login')
|
@Post('/SASLogon/login')
|
||||||
public async loginPost(): Promise<Sas9Response> {
|
public async loginPost(req: express.Request): Promise<Sas9Response> {
|
||||||
this.loggedIn = true
|
this.loggedIn = req.body.username
|
||||||
|
|
||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
@@ -91,8 +115,18 @@ export class MockSas9Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('/SASLogon/logout')
|
@Get('/SASLogon/logout')
|
||||||
public async logout(): Promise<Sas9Response> {
|
public async logout(req: express.Request): Promise<Sas9Response> {
|
||||||
this.loggedIn = false
|
this.loggedIn = undefined
|
||||||
|
|
||||||
|
if (req.query.publicDenied === 'true') {
|
||||||
|
return await getMockResponseFromFile([
|
||||||
|
process.cwd(),
|
||||||
|
'mocks',
|
||||||
|
'generic',
|
||||||
|
'sas9',
|
||||||
|
'public-access-denied'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
return await getMockResponseFromFile([
|
return await getMockResponseFromFile([
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
@@ -102,6 +136,20 @@ export class MockSas9Controller {
|
|||||||
'logged-out'
|
'logged-out'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('/SASStoredProcess/Logoff') //publicDenied=true
|
||||||
|
public async logoff(req: express.Request): Promise<Sas9Response> {
|
||||||
|
const params = req.query.publicDenied
|
||||||
|
? `?publicDenied=${req.query.publicDenied}`
|
||||||
|
: ''
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
redirect: '/SASLogon/logout' + params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isPublicAccount = () => this.loggedIn?.toLowerCase() === 'public'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ interface ExecutePostRequestPayload {
|
|||||||
@Tags('STP')
|
@Tags('STP')
|
||||||
export class STPController {
|
export class STPController {
|
||||||
/**
|
/**
|
||||||
* Trigger a SAS or JS program using the _program URL parameter.
|
* Trigger a Stored Program using the _program URL parameter.
|
||||||
*
|
*
|
||||||
* Accepts URL parameters and file uploads. For more details, see docs:
|
* Accepts URL parameters and file uploads. For more details, see docs:
|
||||||
*
|
*
|
||||||
* https://server.sasjs.io/storedprograms
|
* https://server.sasjs.io/storedprograms
|
||||||
*
|
*
|
||||||
* @summary Execute a Stored Program, returns raw _webout content.
|
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||||
* @param _program Location of SAS or JS code
|
* @param _program Location of code in SASjs Drive
|
||||||
* @example _program "/Projects/myApp/some/program"
|
* @example _program "/Projects/myApp/some/program"
|
||||||
*/
|
*/
|
||||||
@Get('/execute')
|
@Get('/execute')
|
||||||
@@ -43,21 +43,15 @@ export class STPController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger a SAS or JS program using the _program URL parameter.
|
* Trigger a Stored Program using the _program URL parameter.
|
||||||
*
|
*
|
||||||
* Accepts URL parameters and file uploads. For more details, see docs:
|
* Accepts URL parameters and file uploads. For more details, see docs:
|
||||||
*
|
*
|
||||||
* https://server.sasjs.io/storedprograms
|
* https://server.sasjs.io/storedprograms
|
||||||
*
|
*
|
||||||
* The response will be a JSON object with the following root attributes:
|
|
||||||
* log, webout, headers.
|
|
||||||
*
|
*
|
||||||
* The webout attribute will be nested JSON ONLY if the response-header
|
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||||
* contains a content-type of application/json AND it is valid JSON.
|
* @param _program Location of code in SASjs Drive
|
||||||
* Otherwise it will be a stringified version of the webout content.
|
|
||||||
*
|
|
||||||
* @summary Execute a Stored Program, return a JSON object
|
|
||||||
* @param _program Location of SAS or JS code
|
|
||||||
* @example _program "/Projects/myApp/some/program"
|
* @example _program "/Projects/myApp/some/program"
|
||||||
*/
|
*/
|
||||||
@Post('/execute')
|
@Post('/execute')
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
authenticateRefreshToken
|
authenticateRefreshToken
|
||||||
} from '../../middlewares'
|
} from '../../middlewares'
|
||||||
|
|
||||||
import { authorizeValidation, tokenValidation } from '../../utils'
|
import { tokenValidation } from '../../utils'
|
||||||
import { InfoJWT } from '../../types'
|
import { InfoJWT } from '../../types'
|
||||||
|
|
||||||
const authRouter = express.Router()
|
const authRouter = express.Router()
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ sas9WebRouter.post('/SASStoredProcess/do/', async (req, res) => {
|
|||||||
sas9WebRouter.get('/SASLogon/login', async (req, res) => {
|
sas9WebRouter.get('/SASLogon/login', async (req, res) => {
|
||||||
const response = await controller.loginGet()
|
const response = await controller.loginGet()
|
||||||
|
|
||||||
|
if (response.redirect) {
|
||||||
|
res.redirect(response.redirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res.send(response.content)
|
res.send(response.content)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -66,7 +71,12 @@ sas9WebRouter.get('/SASLogon/login', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
sas9WebRouter.post('/SASLogon/login', async (req, res) => {
|
sas9WebRouter.post('/SASLogon/login', async (req, res) => {
|
||||||
const response = await controller.loginPost()
|
const response = await controller.loginPost(req)
|
||||||
|
|
||||||
|
if (response.redirect) {
|
||||||
|
res.redirect(response.redirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res.send(response.content)
|
res.send(response.content)
|
||||||
@@ -76,7 +86,27 @@ sas9WebRouter.post('/SASLogon/login', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
sas9WebRouter.get('/SASLogon/logout', async (req, res) => {
|
sas9WebRouter.get('/SASLogon/logout', async (req, res) => {
|
||||||
const response = await controller.logout()
|
const response = await controller.logout(req)
|
||||||
|
|
||||||
|
if (response.redirect) {
|
||||||
|
res.redirect(response.redirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.send(response.content)
|
||||||
|
} catch (err: any) {
|
||||||
|
res.status(403).send(err.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
sas9WebRouter.get('/SASStoredProcess/Logoff', async (req, res) => {
|
||||||
|
const response = await controller.logoff(req)
|
||||||
|
|
||||||
|
if (response.redirect) {
|
||||||
|
res.redirect(response.redirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res.send(response.content)
|
res.send(response.content)
|
||||||
|
|||||||
34
api/src/utils/getTokensFromDB.ts
Normal file
34
api/src/utils/getTokensFromDB.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
import User from '../model/User'
|
||||||
|
|
||||||
|
export const getTokensFromDB = async (userId: number, clientId: string) => {
|
||||||
|
const user = await User.findOne({ id: userId })
|
||||||
|
if (!user) return
|
||||||
|
|
||||||
|
const currentTokenObj = user.tokens.find(
|
||||||
|
(tokenObj: any) => tokenObj.clientId === clientId
|
||||||
|
)
|
||||||
|
|
||||||
|
if (currentTokenObj) {
|
||||||
|
const accessToken = currentTokenObj.accessToken
|
||||||
|
const refreshToken = currentTokenObj.refreshToken
|
||||||
|
|
||||||
|
const verifiedAccessToken: any = jwt.verify(
|
||||||
|
accessToken,
|
||||||
|
process.secrets.ACCESS_TOKEN_SECRET
|
||||||
|
)
|
||||||
|
|
||||||
|
const verifiedRefreshToken: any = jwt.verify(
|
||||||
|
refreshToken,
|
||||||
|
process.secrets.REFRESH_TOKEN_SECRET
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
verifiedAccessToken?.userId === userId &&
|
||||||
|
verifiedAccessToken?.clientId === clientId &&
|
||||||
|
verifiedRefreshToken?.userId === userId &&
|
||||||
|
verifiedRefreshToken?.clientId === clientId
|
||||||
|
)
|
||||||
|
return { accessToken, refreshToken }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ export * from './getDesktopFields'
|
|||||||
export * from './getPreProgramVariables'
|
export * from './getPreProgramVariables'
|
||||||
export * from './getRunTimeAndFilePath'
|
export * from './getRunTimeAndFilePath'
|
||||||
export * from './getServerUrl'
|
export * from './getServerUrl'
|
||||||
|
export * from './getTokensFromDB'
|
||||||
export * from './instantiateLogger'
|
export * from './instantiateLogger'
|
||||||
export * from './isDebugOn'
|
export * from './isDebugOn'
|
||||||
export * from './isPublicRoute'
|
export * from './isPublicRoute'
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"description": "Operations about clients"
|
"description": "Operations about clients"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CODE",
|
"name": "Code",
|
||||||
"description": "Execution of code (various runtimes are supported)"
|
"description": "Execution of code (various runtimes are supported)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { RunTimeType } from '../../../context/appContext'
|
||||||
|
|
||||||
export const getLanguageFromExtension = (extension: string) => {
|
export const getLanguageFromExtension = (extension: string) => {
|
||||||
if (extension === 'js') return 'javascript'
|
if (extension === 'js') return 'javascript'
|
||||||
|
|
||||||
@@ -12,3 +14,26 @@ export const getSelection = (editor: any) => {
|
|||||||
const selection = editor?.getModel().getValueInRange(editor?.getSelection())
|
const selection = editor?.getModel().getValueInRange(editor?.getSelection())
|
||||||
return selection ?? ''
|
return selection ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const programPathInjection = (
|
||||||
|
code: string,
|
||||||
|
path: string,
|
||||||
|
runtime: RunTimeType
|
||||||
|
) => {
|
||||||
|
if (path) {
|
||||||
|
if (runtime === RunTimeType.JS) {
|
||||||
|
return `const _PROGRAM = '${path}';\n${code}`
|
||||||
|
}
|
||||||
|
if (runtime === RunTimeType.PY) {
|
||||||
|
return `_PROGRAM = '${path}';\n${code}`
|
||||||
|
}
|
||||||
|
if (runtime === RunTimeType.R) {
|
||||||
|
return `._PROGRAM = '${path}';\n${code}`
|
||||||
|
}
|
||||||
|
if (runtime === RunTimeType.SAS) {
|
||||||
|
return `%let _program = ${path};\n${code}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { DiffEditorDidMount, EditorDidMount, monaco } from 'react-monaco-editor'
|
import { DiffEditorDidMount, EditorDidMount, monaco } from 'react-monaco-editor'
|
||||||
import { SelectChangeEvent } from '@mui/material'
|
import { SelectChangeEvent } from '@mui/material'
|
||||||
import { getSelection } from '../helper'
|
import { getSelection, programPathInjection } from '../helper'
|
||||||
import { AppContext, RunTimeType } from '../../../../context/appContext'
|
import { AppContext, RunTimeType } from '../../../../context/appContext'
|
||||||
import { AlertSeverityType } from '../../../../components/snackbar'
|
import { AlertSeverityType } from '../../../../components/snackbar'
|
||||||
import {
|
import {
|
||||||
@@ -151,7 +151,14 @@ const useEditor = ({
|
|||||||
const runCode = (code: string) => {
|
const runCode = (code: string) => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
axios
|
axios
|
||||||
.post(`/SASjsApi/code/execute`, { code, runTime: selectedRunTime })
|
.post(`/SASjsApi/code/execute`, {
|
||||||
|
code: programPathInjection(
|
||||||
|
code,
|
||||||
|
selectedFilePath,
|
||||||
|
selectedRunTime as RunTimeType
|
||||||
|
),
|
||||||
|
runTime: selectedRunTime
|
||||||
|
})
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
||||||
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
||||||
|
|||||||
Reference in New Issue
Block a user