mirror of
https://github.com/sasjs/server.git
synced 2026-01-16 18:30:06 +00:00
Compare commits
5 Commits
6ae8d4c5d3
...
cpr-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66232aefd2 | ||
|
|
bf35791655 | ||
|
|
2dc11630e4 | ||
|
|
1473925896 | ||
|
|
b472f1bd61 |
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,3 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": ["autoexec", "initialising"]
|
"cSpell.words": [
|
||||||
|
"autoexec"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,50 +1,3 @@
|
|||||||
# [0.39.0](https://github.com/sasjs/server/compare/v0.38.0...v0.39.0) (2024-10-31)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **api:** fixed condition in processProgram ([48a9a4d](https://github.com/sasjs/server/commit/48a9a4dd0e31f84209635382be4ec4bb2c3a9c0c))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **api:** added session state endpoint ([6b6546c](https://github.com/sasjs/server/commit/6b6546c7ad0833347f8dc4cdba6ad19132f7aaef))
|
|
||||||
|
|
||||||
# [0.38.0](https://github.com/sasjs/server/compare/v0.37.0...v0.38.0) (2024-10-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **api:** enabled query params in stp/trigger endpoint ([5cda9cd](https://github.com/sasjs/server/commit/5cda9cd5d8623b7ea2ecd989d7808f47ec866672))
|
|
||||||
|
|
||||||
# [0.37.0](https://github.com/sasjs/server/compare/v0.36.0...v0.37.0) (2024-10-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **stp:** added trigger endpoint ([b0723f1](https://github.com/sasjs/server/commit/b0723f14448d60ffce4f2175cf8a73fc4d4dd0ee))
|
|
||||||
|
|
||||||
# [0.36.0](https://github.com/sasjs/server/compare/v0.35.4...v0.36.0) (2024-10-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **code:** added code/trigger API endpoint ([ffcf193](https://github.com/sasjs/server/commit/ffcf193b87d811b166d79af74013776a253b50b0))
|
|
||||||
|
|
||||||
## [0.35.4](https://github.com/sasjs/server/compare/v0.35.3...v0.35.4) (2024-01-15)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **api:** fixed env issue in MacOS executable ([73d965d](https://github.com/sasjs/server/commit/73d965daf54b16c0921e4b18d11a1e6f8650884d))
|
|
||||||
|
|
||||||
## [0.35.3](https://github.com/sasjs/server/compare/v0.35.2...v0.35.3) (2023-11-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* enable embedded LFs in JS STP vars ([7e8cbbf](https://github.com/sasjs/server/commit/7e8cbbf377b27a7f5dd9af0bc6605c01f302f5d9))
|
|
||||||
|
|
||||||
## [0.35.2](https://github.com/sasjs/server/compare/v0.35.1...v0.35.2) (2023-08-07)
|
## [0.35.2](https://github.com/sasjs/server/compare/v0.35.1...v0.35.2) (2023-08-07)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ LDAP_USERS_BASE_DN = <ou=users,dc=cloudron>
|
|||||||
LDAP_GROUPS_BASE_DN = <ou=groups,dc=cloudron>
|
LDAP_GROUPS_BASE_DN = <ou=groups,dc=cloudron>
|
||||||
|
|
||||||
#default value is 100
|
#default value is 100
|
||||||
MAX_WRONG_ATTEMPTS_BY_IP_PER_DAY=100
|
MAX_WRONG_ATTEMPTS_BY_IP_PER_DAY=100
|
||||||
|
|
||||||
#default value is 10
|
#default value is 10
|
||||||
MAX_CONSECUTIVE_FAILS_BY_USERNAME_AND_IP=10
|
MAX_CONSECUTIVE_FAILS_BY_USERNAME_AND_IP=10
|
||||||
|
|||||||
4659
api/package-lock.json
generated
4659
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,10 +18,10 @@
|
|||||||
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||||
"exe": "npm run build && pkg .",
|
"exe": "npm run build && pkg .",
|
||||||
"copy:files": "npm run public:copy && npm run sasjsbuild:copy && npm run sas:copy && npm run web:copy",
|
"copy:files": "npm run public:copy && npm run sasjsbuild:copy && npm run sas:copy && npm run web:copy",
|
||||||
"public:copy": "cp -r ./public/ ./build/public/",
|
"public:copy": "cpr ./public/ ./build/public/",
|
||||||
"sasjsbuild:copy": "cp -r ./sasjsbuild/ ./build/sasjsbuild/",
|
"sasjsbuild:copy": "cpr ./sasjsbuild/ ./build/sasjsbuild/",
|
||||||
"sas:copy": "cp -r ./sas/ ./build/sas/",
|
"sas:copy": "cpr ./sas/ ./build/sas/",
|
||||||
"web:copy": "rimraf web && mkdir web && cp -r ../web/build/ ./web/build/",
|
"web:copy": "rimraf web && mkdir web && cpr ../web/build/ ./web/build/",
|
||||||
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
||||||
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts",
|
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts",
|
||||||
"downloadMacros": "ts-node ./scripts/downloadMacros.ts"
|
"downloadMacros": "ts-node ./scripts/downloadMacros.ts"
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"helmet": "^5.0.2",
|
"helmet": "^5.0.2",
|
||||||
"joi": "^17.4.2",
|
"joi": "^17.4.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"ldapjs": "2.3.3",
|
"ldapjs": "2.3.3",
|
||||||
"mongoose": "^6.0.12",
|
"mongoose": "^6.0.12",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
@@ -87,15 +87,16 @@
|
|||||||
"@types/unzipper": "^0.10.5",
|
"@types/unzipper": "^0.10.5",
|
||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
"axios": "0.27.2",
|
"axios": "0.27.2",
|
||||||
|
"cpr": "^3.0.1",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^10.0.0",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
"mongodb-memory-server": "8.11.4",
|
"mongodb-memory-server": "8.11.4",
|
||||||
"nodejs-file-downloader": "4.10.2",
|
"nodejs-file-downloader": "4.10.2",
|
||||||
"nodemon": "^2.0.7",
|
"nodemon": "^2.0.7",
|
||||||
"pkg": "5.6.0",
|
"pkg": "5.6.0",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^3.0.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^27.0.3",
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ components:
|
|||||||
clientId:
|
clientId:
|
||||||
type: string
|
type: string
|
||||||
userId:
|
userId:
|
||||||
type: string
|
type: number
|
||||||
|
format: double
|
||||||
required:
|
required:
|
||||||
- clientId
|
- clientId
|
||||||
- userId
|
- userId
|
||||||
@@ -97,47 +98,17 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
code:
|
code:
|
||||||
type: string
|
type: string
|
||||||
description: 'The code to be executed'
|
description: 'Code of program'
|
||||||
example: '* Your Code HERE;'
|
example: '* Code HERE;'
|
||||||
runTime:
|
runTime:
|
||||||
$ref: '#/components/schemas/RunTimeType'
|
$ref: '#/components/schemas/RunTimeType'
|
||||||
description: 'The runtime for the code - eg SAS, JS, PY or R'
|
description: 'runtime for program'
|
||||||
example: js
|
example: js
|
||||||
required:
|
required:
|
||||||
- code
|
- code
|
||||||
- runTime
|
- runTime
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
TriggerCodeResponse:
|
|
||||||
properties:
|
|
||||||
sessionId:
|
|
||||||
type: string
|
|
||||||
description: "`sessionId` is the ID of the session and the name of the temporary folder\nused to store code outputs.<br><br>\nFor SAS, this would be the location of the SASWORK folder.<br><br>\n`sessionId` can be used to poll session state using the\nGET /SASjsApi/session/{sessionId}/state endpoint."
|
|
||||||
example: 20241028074744-54132-1730101664824
|
|
||||||
required:
|
|
||||||
- sessionId
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
TriggerCodePayload:
|
|
||||||
properties:
|
|
||||||
code:
|
|
||||||
type: string
|
|
||||||
description: 'The code to be executed'
|
|
||||||
example: '* Your Code HERE;'
|
|
||||||
runTime:
|
|
||||||
$ref: '#/components/schemas/RunTimeType'
|
|
||||||
description: 'The runtime for the code - eg SAS, JS, PY or R'
|
|
||||||
example: sas
|
|
||||||
expiresAfterMins:
|
|
||||||
type: number
|
|
||||||
format: double
|
|
||||||
description: "Amount of minutes after the completion of the job when the session must be\ndestroyed."
|
|
||||||
example: 15
|
|
||||||
required:
|
|
||||||
- code
|
|
||||||
- runTime
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
MemberType.folder:
|
MemberType.folder:
|
||||||
enum:
|
enum:
|
||||||
- folder
|
- folder
|
||||||
@@ -314,8 +285,9 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
UserResponse:
|
UserResponse:
|
||||||
properties:
|
properties:
|
||||||
uid:
|
id:
|
||||||
type: string
|
type: number
|
||||||
|
format: double
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
displayName:
|
displayName:
|
||||||
@@ -323,7 +295,7 @@ components:
|
|||||||
isAdmin:
|
isAdmin:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- uid
|
- id
|
||||||
- username
|
- username
|
||||||
- displayName
|
- displayName
|
||||||
- isAdmin
|
- isAdmin
|
||||||
@@ -331,30 +303,32 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
GroupResponse:
|
GroupResponse:
|
||||||
properties:
|
properties:
|
||||||
uid:
|
groupId:
|
||||||
type: string
|
type: number
|
||||||
|
format: double
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- uid
|
- groupId
|
||||||
- name
|
- name
|
||||||
- description
|
- description
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
UserDetailsResponse:
|
UserDetailsResponse:
|
||||||
properties:
|
properties:
|
||||||
uid:
|
id:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
displayName:
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
displayName:
|
|
||||||
type: string
|
|
||||||
isAdmin:
|
|
||||||
type: boolean
|
|
||||||
isActive:
|
isActive:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
isAdmin:
|
||||||
|
type: boolean
|
||||||
autoExec:
|
autoExec:
|
||||||
type: string
|
type: string
|
||||||
groups:
|
groups:
|
||||||
@@ -362,11 +336,11 @@ components:
|
|||||||
$ref: '#/components/schemas/GroupResponse'
|
$ref: '#/components/schemas/GroupResponse'
|
||||||
type: array
|
type: array
|
||||||
required:
|
required:
|
||||||
- uid
|
- id
|
||||||
- username
|
|
||||||
- displayName
|
- displayName
|
||||||
- isAdmin
|
- username
|
||||||
- isActive
|
- isActive
|
||||||
|
- isAdmin
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
UserPayload:
|
UserPayload:
|
||||||
@@ -402,8 +376,9 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
GroupDetailsResponse:
|
GroupDetailsResponse:
|
||||||
properties:
|
properties:
|
||||||
uid:
|
groupId:
|
||||||
type: string
|
type: number
|
||||||
|
format: double
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
@@ -415,7 +390,7 @@ components:
|
|||||||
$ref: '#/components/schemas/UserResponse'
|
$ref: '#/components/schemas/UserResponse'
|
||||||
type: array
|
type: array
|
||||||
required:
|
required:
|
||||||
- uid
|
- groupId
|
||||||
- name
|
- name
|
||||||
- description
|
- description
|
||||||
- isActive
|
- isActive
|
||||||
@@ -484,8 +459,9 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
PermissionDetailsResponse:
|
PermissionDetailsResponse:
|
||||||
properties:
|
properties:
|
||||||
uid:
|
permissionId:
|
||||||
type: string
|
type: number
|
||||||
|
format: double
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
@@ -497,7 +473,7 @@ components:
|
|||||||
group:
|
group:
|
||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
required:
|
required:
|
||||||
- uid
|
- permissionId
|
||||||
- path
|
- path
|
||||||
- type
|
- type
|
||||||
- setting
|
- setting
|
||||||
@@ -536,8 +512,10 @@ components:
|
|||||||
description: 'Indicates the type of principal'
|
description: 'Indicates the type of principal'
|
||||||
example: user
|
example: user
|
||||||
principalId:
|
principalId:
|
||||||
type: string
|
type: number
|
||||||
|
format: double
|
||||||
description: 'The id of user or group to which a rule is assigned.'
|
description: 'The id of user or group to which a rule is assigned.'
|
||||||
|
example: 123
|
||||||
required:
|
required:
|
||||||
- path
|
- path
|
||||||
- type
|
- type
|
||||||
@@ -556,47 +534,27 @@ components:
|
|||||||
- setting
|
- setting
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
Pick_UserResponse.Exclude_keyofUserResponse.uid__:
|
|
||||||
properties:
|
|
||||||
username:
|
|
||||||
type: string
|
|
||||||
displayName:
|
|
||||||
type: string
|
|
||||||
isAdmin:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- username
|
|
||||||
- displayName
|
|
||||||
- isAdmin
|
|
||||||
type: object
|
|
||||||
description: 'From T, pick a set of properties whose keys are in the union K'
|
|
||||||
SessionResponse:
|
SessionResponse:
|
||||||
properties:
|
properties:
|
||||||
|
id:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
displayName:
|
displayName:
|
||||||
type: string
|
type: string
|
||||||
isAdmin:
|
isAdmin:
|
||||||
type: boolean
|
type: boolean
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
needsToUpdatePassword:
|
needsToUpdatePassword:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
|
- id
|
||||||
- username
|
- username
|
||||||
- displayName
|
- displayName
|
||||||
- isAdmin
|
- isAdmin
|
||||||
- id
|
- needsToUpdatePassword
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
SessionState:
|
|
||||||
enum:
|
|
||||||
- initialising
|
|
||||||
- pending
|
|
||||||
- running
|
|
||||||
- completed
|
|
||||||
- failed
|
|
||||||
type: string
|
|
||||||
ExecutePostRequestPayload:
|
ExecutePostRequestPayload:
|
||||||
properties:
|
properties:
|
||||||
_program:
|
_program:
|
||||||
@@ -605,16 +563,6 @@ components:
|
|||||||
example: /Public/somefolder/some.file
|
example: /Public/somefolder/some.file
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
TriggerProgramResponse:
|
|
||||||
properties:
|
|
||||||
sessionId:
|
|
||||||
type: string
|
|
||||||
description: "`sessionId` is the ID of the session and the name of the temporary folder\nused to store program outputs.<br><br>\nFor SAS, this would be the location of the SASWORK folder.<br><br>\n`sessionId` can be used to poll session state using the\nGET /SASjsApi/session/{sessionId}/state endpoint."
|
|
||||||
example: 20241028074744-54132-1730101664824
|
|
||||||
required:
|
|
||||||
- sessionId
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
LoginPayload:
|
LoginPayload:
|
||||||
properties:
|
properties:
|
||||||
username:
|
username:
|
||||||
@@ -857,30 +805,6 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ExecuteCodePayload'
|
$ref: '#/components/schemas/ExecuteCodePayload'
|
||||||
/SASjsApi/code/trigger:
|
|
||||||
post:
|
|
||||||
operationId: TriggerCode
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Ok
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/TriggerCodeResponse'
|
|
||||||
description: 'Trigger Code on the Specified Runtime'
|
|
||||||
summary: 'Triggers code and returns SessionId immediately - does not wait for job completion'
|
|
||||||
tags:
|
|
||||||
- Code
|
|
||||||
security:
|
|
||||||
-
|
|
||||||
bearerAuth: []
|
|
||||||
parameters: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/TriggerCodePayload'
|
|
||||||
/SASjsApi/drive/deploy:
|
/SASjsApi/drive/deploy:
|
||||||
post:
|
post:
|
||||||
operationId: Deploy
|
operationId: Deploy
|
||||||
@@ -1282,7 +1206,7 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: [{uid: userIdString, username: johnusername, displayName: John, isAdmin: false}, {uid: anotherUserIdString, username: starkusername, displayName: Stark, isAdmin: true}]
|
value: [{id: 123, username: johnusername, displayName: John, isAdmin: false}, {id: 456, username: starkusername, displayName: Stark, isAdmin: true}]
|
||||||
summary: 'Get list of all users (username, displayname). All users can request this.'
|
summary: 'Get list of all users (username, displayname). All users can request this.'
|
||||||
tags:
|
tags:
|
||||||
- User
|
- User
|
||||||
@@ -1301,7 +1225,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/UserDetailsResponse'
|
$ref: '#/components/schemas/UserDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: userIdString, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
||||||
summary: 'Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task.'
|
summary: 'Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task.'
|
||||||
tags:
|
tags:
|
||||||
- User
|
- User
|
||||||
@@ -1352,7 +1276,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/UserDetailsResponse'
|
$ref: '#/components/schemas/UserDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: userIdString, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
||||||
summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.'
|
summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.'
|
||||||
tags:
|
tags:
|
||||||
- User
|
- User
|
||||||
@@ -1403,7 +1327,7 @@ paths:
|
|||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
'/SASjsApi/user/{uid}':
|
'/SASjsApi/user/{userId}':
|
||||||
get:
|
get:
|
||||||
operationId: GetUser
|
operationId: GetUser
|
||||||
responses:
|
responses:
|
||||||
@@ -1422,12 +1346,14 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
|
description: 'The user''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: uid
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
'/SASjsApi/user/{userId}':
|
type: number
|
||||||
|
example: 1234
|
||||||
patch:
|
patch:
|
||||||
operationId: UpdateUser
|
operationId: UpdateUser
|
||||||
responses:
|
responses:
|
||||||
@@ -1439,7 +1365,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/UserDetailsResponse'
|
$ref: '#/components/schemas/UserDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: userIdString, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
||||||
summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.'
|
summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.'
|
||||||
tags:
|
tags:
|
||||||
- User
|
- User
|
||||||
@@ -1453,7 +1379,8 @@ paths:
|
|||||||
name: userId
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
|
type: number
|
||||||
example: '1234'
|
example: '1234'
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
@@ -1479,7 +1406,8 @@ paths:
|
|||||||
name: userId
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
|
type: number
|
||||||
example: 1234
|
example: 1234
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
@@ -1504,7 +1432,7 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: [{uid: groupIdString, name: DCGroup, description: 'This group represents Data Controller Users'}]
|
value: [{groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users'}]
|
||||||
summary: 'Get list of all groups (groupName and groupDescription). All users can request this.'
|
summary: 'Get list of all groups (groupName and groupDescription). All users can request this.'
|
||||||
tags:
|
tags:
|
||||||
- Group
|
- Group
|
||||||
@@ -1523,7 +1451,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: groupIdString, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||||
summary: 'Create a new group. Admin only.'
|
summary: 'Create a new group. Admin only.'
|
||||||
tags:
|
tags:
|
||||||
- Group
|
- Group
|
||||||
@@ -1539,7 +1467,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupPayload'
|
$ref: '#/components/schemas/GroupPayload'
|
||||||
'/SASjsApi/group/by/groupname/{name}':
|
'/SASjsApi/group/by/groupname/{name}':
|
||||||
get:
|
get:
|
||||||
operationId: GetGroupByName
|
operationId: GetGroupByGroupName
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Ok
|
description: Ok
|
||||||
@@ -1561,7 +1489,7 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
'/SASjsApi/group/{uid}':
|
'/SASjsApi/group/{groupId}':
|
||||||
get:
|
get:
|
||||||
operationId: GetGroup
|
operationId: GetGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1581,11 +1509,12 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: uid
|
name: groupId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
example: 12ByteString
|
type: number
|
||||||
|
example: 1234
|
||||||
delete:
|
delete:
|
||||||
operationId: DeleteGroup
|
operationId: DeleteGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1607,12 +1536,13 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: uid
|
name: groupId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
example: 12ByteString
|
type: number
|
||||||
'/SASjsApi/group/{groupUid}/{userUid}':
|
example: 1234
|
||||||
|
'/SASjsApi/group/{groupId}/{userId}':
|
||||||
post:
|
post:
|
||||||
operationId: AddUserToGroup
|
operationId: AddUserToGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1624,7 +1554,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: groupIdString, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||||
summary: 'Add a user to a group. Admin task only.'
|
summary: 'Add a user to a group. Admin task only.'
|
||||||
tags:
|
tags:
|
||||||
- Group
|
- Group
|
||||||
@@ -1635,18 +1565,21 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: groupUid
|
name: groupId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
example: 12ByteString
|
type: number
|
||||||
|
example: '1234'
|
||||||
-
|
-
|
||||||
description: 'The user''s identifier'
|
description: 'The user''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: userUid
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
|
type: number
|
||||||
|
example: '6789'
|
||||||
delete:
|
delete:
|
||||||
operationId: RemoveUserFromGroup
|
operationId: RemoveUserFromGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1658,8 +1591,8 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: groupIdString, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||||
summary: 'Remove a user from a group. Admin task only.'
|
summary: 'Remove a user to a group. Admin task only.'
|
||||||
tags:
|
tags:
|
||||||
- Group
|
- Group
|
||||||
security:
|
security:
|
||||||
@@ -1669,19 +1602,21 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: groupUid
|
name: groupId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
example: 12ByteString
|
type: number
|
||||||
|
example: '1234'
|
||||||
-
|
-
|
||||||
description: 'The user''s identifier'
|
description: 'The user''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: userUid
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
example: 12ByteString
|
type: number
|
||||||
|
example: '6789'
|
||||||
/SASjsApi/info:
|
/SASjsApi/info:
|
||||||
get:
|
get:
|
||||||
operationId: Info
|
operationId: Info
|
||||||
@@ -1732,7 +1667,7 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: [{uid: permissionId1String, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {uid: user1-id, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}, {uid: permissionId2String, path: /SASjsApi/code/execute, type: Route, setting: Grant, group: {uid: group1-id, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}}]
|
value: [{permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}, {permissionId: 124, path: /SASjsApi/code/execute, type: Route, setting: Grant, group: {groupId: 1, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}}]
|
||||||
description: "Get the list of permission rules applicable the authenticated user.\nIf the user is an admin, all rules are returned."
|
description: "Get the list of permission rules applicable the authenticated user.\nIf the user is an admin, all rules are returned."
|
||||||
summary: 'Get the list of permission rules. If the user is admin, all rules are returned.'
|
summary: 'Get the list of permission rules. If the user is admin, all rules are returned.'
|
||||||
tags:
|
tags:
|
||||||
@@ -1752,7 +1687,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/PermissionDetailsResponse'
|
$ref: '#/components/schemas/PermissionDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: permissionIdString, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {uid: userIdString, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
value: {permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
||||||
summary: 'Create a new permission. Admin only.'
|
summary: 'Create a new permission. Admin only.'
|
||||||
tags:
|
tags:
|
||||||
- Permission
|
- Permission
|
||||||
@@ -1766,7 +1701,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RegisterPermissionPayload'
|
$ref: '#/components/schemas/RegisterPermissionPayload'
|
||||||
'/SASjsApi/permission/{uid}':
|
'/SASjsApi/permission/{permissionId}':
|
||||||
patch:
|
patch:
|
||||||
operationId: UpdatePermission
|
operationId: UpdatePermission
|
||||||
responses:
|
responses:
|
||||||
@@ -1778,7 +1713,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/PermissionDetailsResponse'
|
$ref: '#/components/schemas/PermissionDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {uid: permissionIdString, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {uid: userIdString, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
value: {permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
||||||
summary: 'Update permission setting. Admin only'
|
summary: 'Update permission setting. Admin only'
|
||||||
tags:
|
tags:
|
||||||
- Permission
|
- Permission
|
||||||
@@ -1787,11 +1722,14 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
|
description: 'The permission''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: uid
|
name: permissionId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
|
type: number
|
||||||
|
example: 1234
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@@ -1811,11 +1749,14 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
|
description: 'The user''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: uid
|
name: permissionId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
format: double
|
||||||
|
type: number
|
||||||
|
example: 1234
|
||||||
/SASjsApi/session:
|
/SASjsApi/session:
|
||||||
get:
|
get:
|
||||||
operationId: Session
|
operationId: Session
|
||||||
@@ -1828,7 +1769,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/SessionResponse'
|
$ref: '#/components/schemas/SessionResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {id: userIdString, username: johnusername, displayName: John, isAdmin: false, needsToUpdatePassword: false}
|
value: {id: 123, username: johnusername, displayName: John, isAdmin: false}
|
||||||
summary: 'Get session info (username).'
|
summary: 'Get session info (username).'
|
||||||
tags:
|
tags:
|
||||||
- Session
|
- Session
|
||||||
@@ -1836,30 +1777,6 @@ paths:
|
|||||||
-
|
-
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters: []
|
parameters: []
|
||||||
'/SASjsApi/session/{sessionId}/state':
|
|
||||||
get:
|
|
||||||
operationId: SessionState
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Ok
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SessionState'
|
|
||||||
description: "The polling endpoint is currently implemented for single-server deployments only.<br>\nLoad balanced / grid topologies will be supported in a future release.<br>\nIf your site requires this, please reach out to SASjs Support."
|
|
||||||
summary: 'Get session state (initialising, pending, running, completed, failed).'
|
|
||||||
tags:
|
|
||||||
- Session
|
|
||||||
security:
|
|
||||||
-
|
|
||||||
bearerAuth: []
|
|
||||||
parameters:
|
|
||||||
-
|
|
||||||
in: path
|
|
||||||
name: sessionId
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
/SASjsApi/stp/execute:
|
/SASjsApi/stp/execute:
|
||||||
get:
|
get:
|
||||||
operationId: ExecuteGetRequest
|
operationId: ExecuteGetRequest
|
||||||
@@ -1881,7 +1798,7 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'Location of Stored Program in SASjs Drive.'
|
description: 'Location of code in SASjs Drive'
|
||||||
in: query
|
in: query
|
||||||
name: _program
|
name: _program
|
||||||
required: true
|
required: true
|
||||||
@@ -1889,7 +1806,7 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
example: /Projects/myApp/some/program
|
example: /Projects/myApp/some/program
|
||||||
-
|
-
|
||||||
description: 'Optional query param for setting debug mode (returns the session log in the response body).'
|
description: 'Optional query param for setting debug mode, which will return the session log.'
|
||||||
in: query
|
in: query
|
||||||
name: _debug
|
name: _debug
|
||||||
required: false
|
required: false
|
||||||
@@ -1930,50 +1847,6 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ExecutePostRequestPayload'
|
$ref: '#/components/schemas/ExecutePostRequestPayload'
|
||||||
/SASjsApi/stp/trigger:
|
|
||||||
post:
|
|
||||||
operationId: TriggerProgram
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Ok
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/TriggerProgramResponse'
|
|
||||||
description: 'Trigger Program on the Specified Runtime.'
|
|
||||||
summary: 'Triggers program and returns SessionId immediately - does not wait for program completion.'
|
|
||||||
tags:
|
|
||||||
- STP
|
|
||||||
security:
|
|
||||||
-
|
|
||||||
bearerAuth: []
|
|
||||||
parameters:
|
|
||||||
-
|
|
||||||
description: 'Location of code in SASjs Drive.'
|
|
||||||
in: query
|
|
||||||
name: _program
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: /Projects/myApp/some/program
|
|
||||||
-
|
|
||||||
description: 'Optional query param for setting debug mode.'
|
|
||||||
in: query
|
|
||||||
name: _debug
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
format: double
|
|
||||||
type: number
|
|
||||||
example: 131
|
|
||||||
-
|
|
||||||
description: 'Optional query param for setting amount of minutes after the completion of the program when the session must be destroyed.'
|
|
||||||
in: query
|
|
||||||
name: expiresAfterMins
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
format: double
|
|
||||||
type: number
|
|
||||||
example: 15
|
|
||||||
/:
|
/:
|
||||||
get:
|
get:
|
||||||
operationId: Home
|
operationId: Home
|
||||||
@@ -1999,7 +1872,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
properties:
|
properties:
|
||||||
user: {properties: {needsToUpdatePassword: {type: boolean}, isAdmin: {type: boolean}, displayName: {type: string}, username: {type: string}, id: {}}, required: [needsToUpdatePassword, isAdmin, displayName, username, id], type: object}
|
user: {properties: {needsToUpdatePassword: {type: boolean}, isAdmin: {type: boolean}, displayName: {type: string}, username: {type: string}, id: {type: number, format: double}}, required: [needsToUpdatePassword, isAdmin, displayName, username, id], type: object}
|
||||||
loggedIn: {type: boolean}
|
loggedIn: {type: boolean}
|
||||||
required:
|
required:
|
||||||
- user
|
- user
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ import User from '../model/User'
|
|||||||
@Tags('Auth')
|
@Tags('Auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
static authCodes: { [key: string]: { [key: string]: string } } = {}
|
static authCodes: { [key: string]: { [key: string]: string } } = {}
|
||||||
static saveCode = (userId: string, clientId: string, code: string) => {
|
static saveCode = (userId: number, clientId: string, code: string) => {
|
||||||
if (AuthController.authCodes[userId])
|
if (AuthController.authCodes[userId])
|
||||||
return (AuthController.authCodes[userId][clientId] = code)
|
return (AuthController.authCodes[userId][clientId] = code)
|
||||||
|
|
||||||
AuthController.authCodes[userId] = { [clientId]: code }
|
AuthController.authCodes[userId] = { [clientId]: code }
|
||||||
return AuthController.authCodes[userId][clientId]
|
return AuthController.authCodes[userId][clientId]
|
||||||
}
|
}
|
||||||
static deleteCode = (userId: string, clientId: string) =>
|
static deleteCode = (userId: number, clientId: string) =>
|
||||||
delete AuthController.authCodes[userId][clientId]
|
delete AuthController.authCodes[userId][clientId]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +159,7 @@ const updatePassword = async (
|
|||||||
) => {
|
) => {
|
||||||
const { currentPassword, newPassword } = data
|
const { currentPassword, newPassword } = data
|
||||||
const userId = req.user?.userId
|
const userId = req.user?.userId
|
||||||
const dbUser = await User.findOne({ _id: userId })
|
const dbUser = await User.findOne({ id: userId })
|
||||||
|
|
||||||
if (!dbUser)
|
if (!dbUser)
|
||||||
throw {
|
throw {
|
||||||
|
|||||||
@@ -1,57 +1,27 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { Request, Security, Route, Tags, Post, Body } from 'tsoa'
|
import { Request, Security, Route, Tags, Post, Body } from 'tsoa'
|
||||||
import { ExecutionController, getSessionController } from './internal'
|
import { ExecutionController } from './internal'
|
||||||
import {
|
import {
|
||||||
getPreProgramVariables,
|
getPreProgramVariables,
|
||||||
getUserAutoExec,
|
getUserAutoExec,
|
||||||
ModeType,
|
ModeType,
|
||||||
|
parseLogToArray,
|
||||||
RunTimeType
|
RunTimeType
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
interface ExecuteCodePayload {
|
interface ExecuteCodePayload {
|
||||||
/**
|
/**
|
||||||
* The code to be executed
|
* Code of program
|
||||||
* @example "* Your Code HERE;"
|
* @example "* Code HERE;"
|
||||||
*/
|
*/
|
||||||
code: string
|
code: string
|
||||||
/**
|
/**
|
||||||
* The runtime for the code - eg SAS, JS, PY or R
|
* runtime for program
|
||||||
* @example "js"
|
* @example "js"
|
||||||
*/
|
*/
|
||||||
runTime: RunTimeType
|
runTime: RunTimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TriggerCodePayload {
|
|
||||||
/**
|
|
||||||
* The code to be executed
|
|
||||||
* @example "* Your Code HERE;"
|
|
||||||
*/
|
|
||||||
code: string
|
|
||||||
/**
|
|
||||||
* The runtime for the code - eg SAS, JS, PY or R
|
|
||||||
* @example "sas"
|
|
||||||
*/
|
|
||||||
runTime: RunTimeType
|
|
||||||
/**
|
|
||||||
* Amount of minutes after the completion of the job when the session must be
|
|
||||||
* destroyed.
|
|
||||||
* @example 15
|
|
||||||
*/
|
|
||||||
expiresAfterMins?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TriggerCodeResponse {
|
|
||||||
/**
|
|
||||||
* `sessionId` is the ID of the session and the name of the temporary folder
|
|
||||||
* used to store code outputs.<br><br>
|
|
||||||
* For SAS, this would be the location of the SASWORK folder.<br><br>
|
|
||||||
* `sessionId` can be used to poll session state using the
|
|
||||||
* GET /SASjsApi/session/{sessionId}/state endpoint.
|
|
||||||
* @example "20241028074744-54132-1730101664824"
|
|
||||||
*/
|
|
||||||
sessionId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/code')
|
@Route('SASjsApi/code')
|
||||||
@Tags('Code')
|
@Tags('Code')
|
||||||
@@ -74,18 +44,6 @@ export class CodeController {
|
|||||||
): Promise<string | Buffer> {
|
): Promise<string | Buffer> {
|
||||||
return executeCode(request, body)
|
return executeCode(request, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger Code on the Specified Runtime
|
|
||||||
* @summary Triggers code and returns SessionId immediately - does not wait for job completion
|
|
||||||
*/
|
|
||||||
@Post('/trigger')
|
|
||||||
public async triggerCode(
|
|
||||||
@Request() request: express.Request,
|
|
||||||
@Body() body: TriggerCodePayload
|
|
||||||
): Promise<TriggerCodeResponse> {
|
|
||||||
return triggerCode(request, body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeCode = async (
|
const executeCode = async (
|
||||||
@@ -118,49 +76,3 @@ const executeCode = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerCode = async (
|
|
||||||
req: express.Request,
|
|
||||||
{ code, runTime, expiresAfterMins }: TriggerCodePayload
|
|
||||||
): Promise<TriggerCodeResponse> => {
|
|
||||||
const { user } = req
|
|
||||||
const userAutoExec =
|
|
||||||
process.env.MODE === ModeType.Server
|
|
||||||
? user?.autoExec
|
|
||||||
: await getUserAutoExec()
|
|
||||||
|
|
||||||
// get session controller based on runTime
|
|
||||||
const sessionController = getSessionController(runTime)
|
|
||||||
|
|
||||||
// get session
|
|
||||||
const session = await sessionController.getSession()
|
|
||||||
|
|
||||||
// add expiresAfterMins to session if provided
|
|
||||||
if (expiresAfterMins) {
|
|
||||||
// expiresAfterMins.used is set initially to false
|
|
||||||
session.expiresAfterMins = { mins: expiresAfterMins, used: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// call executeProgram method of ExecutionController without awaiting
|
|
||||||
new ExecutionController().executeProgram({
|
|
||||||
program: code,
|
|
||||||
preProgramVariables: getPreProgramVariables(req),
|
|
||||||
vars: { ...req.query, _debug: 131 },
|
|
||||||
otherArgs: { userAutoExec },
|
|
||||||
runTime: runTime,
|
|
||||||
includePrintOutput: true,
|
|
||||||
session // session is provided
|
|
||||||
})
|
|
||||||
|
|
||||||
// return session id
|
|
||||||
return { sessionId: session.id }
|
|
||||||
} catch (err: any) {
|
|
||||||
throw {
|
|
||||||
code: 400,
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Job execution failed.',
|
|
||||||
error: typeof err === 'object' ? err.toString() : err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,29 +12,28 @@ import {
|
|||||||
|
|
||||||
import Group, { GroupPayload, PUBLIC_GROUP_NAME } from '../model/Group'
|
import Group, { GroupPayload, PUBLIC_GROUP_NAME } from '../model/Group'
|
||||||
import User from '../model/User'
|
import User from '../model/User'
|
||||||
import { GetUserBy, UserResponse } from './user'
|
import { AuthProviderType } from '../utils'
|
||||||
|
import { UserResponse } from './user'
|
||||||
|
|
||||||
export interface GroupResponse {
|
export interface GroupResponse {
|
||||||
uid: string
|
groupId: number
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupDetailsResponse extends GroupResponse {
|
export interface GroupDetailsResponse {
|
||||||
|
groupId: number
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
users: UserResponse[]
|
users: UserResponse[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetGroupBy {
|
interface GetGroupBy {
|
||||||
_id?: string
|
groupId?: number
|
||||||
name?: string
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GroupAction {
|
|
||||||
AddUser = 'addUser',
|
|
||||||
RemoveUser = 'removeUser'
|
|
||||||
}
|
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/group')
|
@Route('SASjsApi/group')
|
||||||
@Tags('Group')
|
@Tags('Group')
|
||||||
@@ -45,7 +44,7 @@ export class GroupController {
|
|||||||
*/
|
*/
|
||||||
@Example<GroupResponse[]>([
|
@Example<GroupResponse[]>([
|
||||||
{
|
{
|
||||||
uid: 'groupIdString',
|
groupId: 123,
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users'
|
description: 'This group represents Data Controller Users'
|
||||||
}
|
}
|
||||||
@@ -60,7 +59,7 @@ export class GroupController {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<GroupDetailsResponse>({
|
@Example<GroupDetailsResponse>({
|
||||||
uid: 'groupIdString',
|
groupId: 123,
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users',
|
description: 'This group represents Data Controller Users',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
@@ -79,7 +78,7 @@ export class GroupController {
|
|||||||
* @example dcgroup
|
* @example dcgroup
|
||||||
*/
|
*/
|
||||||
@Get('by/groupname/{name}')
|
@Get('by/groupname/{name}')
|
||||||
public async getGroupByName(
|
public async getGroupByGroupName(
|
||||||
@Path() name: string
|
@Path() name: string
|
||||||
): Promise<GroupDetailsResponse> {
|
): Promise<GroupDetailsResponse> {
|
||||||
return getGroup({ name })
|
return getGroup({ name })
|
||||||
@@ -87,66 +86,68 @@ export class GroupController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get list of members of a group (userName). All users can request this.
|
* @summary Get list of members of a group (userName). All users can request this.
|
||||||
* @param uid The group's identifier
|
* @param groupId The group's identifier
|
||||||
* @example uid "12ByteString"
|
* @example groupId 1234
|
||||||
*/
|
*/
|
||||||
@Get('{uid}')
|
@Get('{groupId}')
|
||||||
public async getGroup(@Path() uid: string): Promise<GroupDetailsResponse> {
|
public async getGroup(
|
||||||
return getGroup({ _id: uid })
|
@Path() groupId: number
|
||||||
|
): Promise<GroupDetailsResponse> {
|
||||||
|
return getGroup({ groupId })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Add a user to a group. Admin task only.
|
* @summary Add a user to a group. Admin task only.
|
||||||
* @param groupUid The group's identifier
|
* @param groupId The group's identifier
|
||||||
* @example groupUid "12ByteString"
|
* @example groupId "1234"
|
||||||
* @param userUid The user's identifier
|
* @param userId The user's identifier
|
||||||
* @example userId "12ByteString"
|
* @example userId "6789"
|
||||||
*/
|
*/
|
||||||
@Example<GroupDetailsResponse>({
|
@Example<GroupDetailsResponse>({
|
||||||
uid: 'groupIdString',
|
groupId: 123,
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users',
|
description: 'This group represents Data Controller Users',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
users: []
|
users: []
|
||||||
})
|
})
|
||||||
@Post('{groupUid}/{userUid}')
|
@Post('{groupId}/{userId}')
|
||||||
public async addUserToGroup(
|
public async addUserToGroup(
|
||||||
@Path() groupUid: string,
|
@Path() groupId: number,
|
||||||
@Path() userUid: string
|
@Path() userId: number
|
||||||
): Promise<GroupDetailsResponse> {
|
): Promise<GroupDetailsResponse> {
|
||||||
return addUserToGroup(groupUid, userUid)
|
return addUserToGroup(groupId, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Remove a user from a group. Admin task only.
|
* @summary Remove a user to a group. Admin task only.
|
||||||
* @param groupUid The group's identifier
|
* @param groupId The group's identifier
|
||||||
* @example groupUid "12ByteString"
|
* @example groupId "1234"
|
||||||
* @param userUid The user's identifier
|
* @param userId The user's identifier
|
||||||
* @example userUid "12ByteString"
|
* @example userId "6789"
|
||||||
*/
|
*/
|
||||||
@Example<GroupDetailsResponse>({
|
@Example<GroupDetailsResponse>({
|
||||||
uid: 'groupIdString',
|
groupId: 123,
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users',
|
description: 'This group represents Data Controller Users',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
users: []
|
users: []
|
||||||
})
|
})
|
||||||
@Delete('{groupUid}/{userUid}')
|
@Delete('{groupId}/{userId}')
|
||||||
public async removeUserFromGroup(
|
public async removeUserFromGroup(
|
||||||
@Path() groupUid: string,
|
@Path() groupId: number,
|
||||||
@Path() userUid: string
|
@Path() userId: number
|
||||||
): Promise<GroupDetailsResponse> {
|
): Promise<GroupDetailsResponse> {
|
||||||
return removeUserFromGroup(groupUid, userUid)
|
return removeUserFromGroup(groupId, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Delete a group. Admin task only.
|
* @summary Delete a group. Admin task only.
|
||||||
* @param uid The group's identifier
|
* @param groupId The group's identifier
|
||||||
* @example uid "12ByteString"
|
* @example groupId 1234
|
||||||
*/
|
*/
|
||||||
@Delete('{uid}')
|
@Delete('{groupId}')
|
||||||
public async deleteGroup(@Path() uid: string) {
|
public async deleteGroup(@Path() groupId: number) {
|
||||||
const group = await Group.findOne({ _id: uid })
|
const group = await Group.findOne({ groupId })
|
||||||
if (!group)
|
if (!group)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -159,7 +160,9 @@ export class GroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAllGroups = async (): Promise<GroupResponse[]> =>
|
const getAllGroups = async (): Promise<GroupResponse[]> =>
|
||||||
await Group.find({}).select('uid name description').exec()
|
await Group.find({})
|
||||||
|
.select({ _id: 0, groupId: 1, name: 1, description: 1 })
|
||||||
|
.exec()
|
||||||
|
|
||||||
const createGroup = async ({
|
const createGroup = async ({
|
||||||
name,
|
name,
|
||||||
@@ -184,7 +187,7 @@ const createGroup = async ({
|
|||||||
const savedGroup = await group.save()
|
const savedGroup = await group.save()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: savedGroup.uid,
|
groupId: savedGroup.groupId,
|
||||||
name: savedGroup.name,
|
name: savedGroup.name,
|
||||||
description: savedGroup.description,
|
description: savedGroup.description,
|
||||||
isActive: savedGroup.isActive,
|
isActive: savedGroup.isActive,
|
||||||
@@ -195,12 +198,11 @@ const createGroup = async ({
|
|||||||
const getGroup = async (findBy: GetGroupBy): Promise<GroupDetailsResponse> => {
|
const getGroup = async (findBy: GetGroupBy): Promise<GroupDetailsResponse> => {
|
||||||
const group = (await Group.findOne(
|
const group = (await Group.findOne(
|
||||||
findBy,
|
findBy,
|
||||||
'uid name description isActive users'
|
'groupId name description isActive users -_id'
|
||||||
).populate(
|
).populate(
|
||||||
'users',
|
'users',
|
||||||
'uid username displayName isAdmin'
|
'id username displayName isAdmin -_id'
|
||||||
)) as unknown as GroupDetailsResponse
|
)) as unknown as GroupDetailsResponse
|
||||||
|
|
||||||
if (!group)
|
if (!group)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -209,7 +211,7 @@ const getGroup = async (findBy: GetGroupBy): Promise<GroupDetailsResponse> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: group.uid,
|
groupId: group.groupId,
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description,
|
description: group.description,
|
||||||
isActive: group.isActive,
|
isActive: group.isActive,
|
||||||
@@ -218,23 +220,23 @@ const getGroup = async (findBy: GetGroupBy): Promise<GroupDetailsResponse> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addUserToGroup = async (
|
const addUserToGroup = async (
|
||||||
groupUid: string,
|
groupId: number,
|
||||||
userUid: string
|
userId: number
|
||||||
): Promise<GroupDetailsResponse> =>
|
): Promise<GroupDetailsResponse> =>
|
||||||
updateUsersListInGroup(groupUid, userUid, GroupAction.AddUser)
|
updateUsersListInGroup(groupId, userId, 'addUser')
|
||||||
|
|
||||||
const removeUserFromGroup = async (
|
const removeUserFromGroup = async (
|
||||||
groupUid: string,
|
groupId: number,
|
||||||
userUid: string
|
userId: number
|
||||||
): Promise<GroupDetailsResponse> =>
|
): Promise<GroupDetailsResponse> =>
|
||||||
updateUsersListInGroup(groupUid, userUid, GroupAction.RemoveUser)
|
updateUsersListInGroup(groupId, userId, 'removeUser')
|
||||||
|
|
||||||
const updateUsersListInGroup = async (
|
const updateUsersListInGroup = async (
|
||||||
groupUid: string,
|
groupId: number,
|
||||||
userUid: string,
|
userId: number,
|
||||||
action: GroupAction
|
action: 'addUser' | 'removeUser'
|
||||||
): Promise<GroupDetailsResponse> => {
|
): Promise<GroupDetailsResponse> => {
|
||||||
const group = await Group.findOne({ _id: groupUid })
|
const group = await Group.findOne({ groupId })
|
||||||
if (!group)
|
if (!group)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -256,7 +258,7 @@ const updateUsersListInGroup = async (
|
|||||||
message: `Can't add/remove user to group created by external auth provider.`
|
message: `Can't add/remove user to group created by external auth provider.`
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.findOne({ _id: userUid })
|
const user = await User.findOne({ id: userId })
|
||||||
if (!user)
|
if (!user)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -272,7 +274,7 @@ const updateUsersListInGroup = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updatedGroup =
|
const updatedGroup =
|
||||||
action === GroupAction.AddUser
|
action === 'addUser'
|
||||||
? await group.addUser(user)
|
? await group.addUser(user)
|
||||||
: await group.removeUser(user)
|
: await group.removeUser(user)
|
||||||
|
|
||||||
@@ -284,7 +286,7 @@ const updateUsersListInGroup = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: updatedGroup.uid,
|
groupId: updatedGroup.groupId,
|
||||||
name: updatedGroup.name,
|
name: updatedGroup.name,
|
||||||
description: updatedGroup.description,
|
description: updatedGroup.description,
|
||||||
isActive: updatedGroup.isActive,
|
isActive: updatedGroup.isActive,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import path from 'path'
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { getSessionController, processProgram } from './'
|
import { getSessionController, processProgram } from './'
|
||||||
import { readFile, fileExists, createFile, readFileBinary } from '@sasjs/utils'
|
import { readFile, fileExists, createFile, readFileBinary } from '@sasjs/utils'
|
||||||
import { PreProgramVars, Session, TreeNode, SessionState } from '../../types'
|
import { PreProgramVars, Session, TreeNode } from '../../types'
|
||||||
import {
|
import {
|
||||||
extractHeaders,
|
extractHeaders,
|
||||||
getFilesFolder,
|
getFilesFolder,
|
||||||
@@ -75,7 +75,8 @@ export class ExecutionController {
|
|||||||
|
|
||||||
const session =
|
const session =
|
||||||
sessionByFileUpload ?? (await sessionController.getSession())
|
sessionByFileUpload ?? (await sessionController.getSession())
|
||||||
session.state = SessionState.running
|
session.inUse = true
|
||||||
|
session.consumed = true
|
||||||
|
|
||||||
const logPath = path.join(session.path, 'log.log')
|
const logPath = path.join(session.path, 'log.log')
|
||||||
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
||||||
@@ -120,7 +121,7 @@ export class ExecutionController {
|
|||||||
: ''
|
: ''
|
||||||
|
|
||||||
// it should be deleted by scheduleSessionDestroy
|
// it should be deleted by scheduleSessionDestroy
|
||||||
session.state = SessionState.completed
|
session.inUse = false
|
||||||
|
|
||||||
const resultParts = []
|
const resultParts = []
|
||||||
|
|
||||||
@@ -144,9 +145,7 @@ export class ExecutionController {
|
|||||||
return {
|
return {
|
||||||
httpHeaders,
|
httpHeaders,
|
||||||
result:
|
result:
|
||||||
isDebugOn(vars) || session.failureReason
|
isDebugOn(vars) || session.crashed ? resultParts.join(`\n`) : webout
|
||||||
? resultParts.join(`\n`)
|
|
||||||
: webout
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { Request, RequestHandler } from 'express'
|
|||||||
import multer from 'multer'
|
import multer from 'multer'
|
||||||
import { uuidv4 } from '@sasjs/utils'
|
import { uuidv4 } from '@sasjs/utils'
|
||||||
import { getSessionController } from '.'
|
import { getSessionController } from '.'
|
||||||
import { executeProgramRawValidation, getRunTimeAndFilePath } from '../../utils'
|
import {
|
||||||
import { SessionState } from '../../types'
|
executeProgramRawValidation,
|
||||||
|
getRunTimeAndFilePath,
|
||||||
|
RunTimeType
|
||||||
|
} from '../../utils'
|
||||||
|
|
||||||
export class FileUploadController {
|
export class FileUploadController {
|
||||||
private storage = multer.diskStorage({
|
private storage = multer.diskStorage({
|
||||||
@@ -53,8 +56,9 @@ export class FileUploadController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const session = await sessionController.getSession()
|
const session = await sessionController.getSession()
|
||||||
// change session state to 'running', so that it's not available for any other request
|
// marking consumed true, so that it's not available
|
||||||
session.state = SessionState.running
|
// as readySession for any other request
|
||||||
|
session.consumed = true
|
||||||
|
|
||||||
req.sasjsSession = session
|
req.sasjsSession = session
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { Session, SessionState } from '../../types'
|
import { Session } from '../../types'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { execFile } from 'child_process'
|
import { execFile } from 'child_process'
|
||||||
import {
|
import {
|
||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
createFile,
|
createFile,
|
||||||
fileExists,
|
fileExists,
|
||||||
generateTimestamp,
|
generateTimestamp,
|
||||||
readFile
|
readFile,
|
||||||
|
isWindows
|
||||||
} from '@sasjs/utils'
|
} from '@sasjs/utils'
|
||||||
|
|
||||||
const execFilePromise = promisify(execFile)
|
const execFilePromise = promisify(execFile)
|
||||||
@@ -23,9 +24,7 @@ export class SessionController {
|
|||||||
protected sessions: Session[] = []
|
protected sessions: Session[] = []
|
||||||
|
|
||||||
protected getReadySessions = (): Session[] =>
|
protected getReadySessions = (): Session[] =>
|
||||||
this.sessions.filter(
|
this.sessions.filter((sess: Session) => sess.ready && !sess.consumed)
|
||||||
(session: Session) => session.state === SessionState.pending
|
|
||||||
)
|
|
||||||
|
|
||||||
protected async createSession(): Promise<Session> {
|
protected async createSession(): Promise<Session> {
|
||||||
const sessionId = generateUniqueFileName(generateTimestamp())
|
const sessionId = generateUniqueFileName(generateTimestamp())
|
||||||
@@ -41,18 +40,19 @@ export class SessionController {
|
|||||||
|
|
||||||
const session: Session = {
|
const session: Session = {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
state: SessionState.pending,
|
ready: true,
|
||||||
|
inUse: true,
|
||||||
|
consumed: false,
|
||||||
|
completed: false,
|
||||||
creationTimeStamp,
|
creationTimeStamp,
|
||||||
deathTimeStamp,
|
deathTimeStamp,
|
||||||
path: sessionFolder
|
path: sessionFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
||||||
|
|
||||||
await createFile(headersPath, 'content-type: text/html; charset=utf-8')
|
await createFile(headersPath, 'content-type: text/html; charset=utf-8')
|
||||||
|
|
||||||
this.sessions.push(session)
|
this.sessions.push(session)
|
||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +67,6 @@ export class SessionController {
|
|||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSessionById(id: string) {
|
|
||||||
return this.sessions.find((session) => session.id === id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SASSessionController extends SessionController {
|
export class SASSessionController extends SessionController {
|
||||||
@@ -88,7 +84,10 @@ export class SASSessionController extends SessionController {
|
|||||||
|
|
||||||
const session: Session = {
|
const session: Session = {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
state: SessionState.initialising,
|
ready: false,
|
||||||
|
inUse: false,
|
||||||
|
consumed: false,
|
||||||
|
completed: false,
|
||||||
creationTimeStamp,
|
creationTimeStamp,
|
||||||
deathTimeStamp,
|
deathTimeStamp,
|
||||||
path: sessionFolder
|
path: sessionFolder
|
||||||
@@ -146,20 +145,13 @@ ${autoExecContent}`
|
|||||||
process.sasLoc!.endsWith('sas.exe') ? session.path : ''
|
process.sasLoc!.endsWith('sas.exe') ? session.path : ''
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
session.state = SessionState.completed
|
session.completed = true
|
||||||
|
|
||||||
process.logger.info('session completed', session)
|
process.logger.info('session completed', session)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
session.state = SessionState.failed
|
session.completed = true
|
||||||
|
session.crashed = err.toString()
|
||||||
session.failureReason = err.toString()
|
process.logger.error('session crashed', session.id, session.crashed)
|
||||||
|
|
||||||
process.logger.error(
|
|
||||||
'session crashed',
|
|
||||||
session.id,
|
|
||||||
session.failureReason
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// we have a triggered session - add to array
|
// we have a triggered session - add to array
|
||||||
@@ -176,19 +168,15 @@ ${autoExecContent}`
|
|||||||
const codeFilePath = path.join(session.path, 'code.sas')
|
const codeFilePath = path.join(session.path, 'code.sas')
|
||||||
|
|
||||||
// TODO: don't wait forever
|
// TODO: don't wait forever
|
||||||
while (
|
while ((await fileExists(codeFilePath)) && !session.crashed) {}
|
||||||
(await fileExists(codeFilePath)) &&
|
|
||||||
session.state !== SessionState.failed
|
|
||||||
) {}
|
|
||||||
|
|
||||||
if (session.state === SessionState.failed) {
|
if (session.crashed)
|
||||||
process.logger.error(
|
process.logger.error(
|
||||||
'session crashed! while waiting to be ready',
|
'session crashed! while waiting to be ready',
|
||||||
session.failureReason
|
session.crashed
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
session.state = SessionState.pending
|
session.ready = true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteSession(session: Session) {
|
private async deleteSession(session: Session) {
|
||||||
@@ -204,31 +192,14 @@ ${autoExecContent}`
|
|||||||
private scheduleSessionDestroy(session: Session) {
|
private scheduleSessionDestroy(session: Session) {
|
||||||
setTimeout(
|
setTimeout(
|
||||||
async () => {
|
async () => {
|
||||||
if (session.state === SessionState.running) {
|
if (session.inUse) {
|
||||||
// adding 10 more minutes
|
// adding 10 more minutes
|
||||||
const newDeathTimeStamp =
|
const newDeathTimeStamp = parseInt(session.deathTimeStamp) + 10 * 1000
|
||||||
parseInt(session.deathTimeStamp) + 10 * 60 * 1000
|
|
||||||
session.deathTimeStamp = newDeathTimeStamp.toString()
|
session.deathTimeStamp = newDeathTimeStamp.toString()
|
||||||
|
|
||||||
this.scheduleSessionDestroy(session)
|
this.scheduleSessionDestroy(session)
|
||||||
} else {
|
} else {
|
||||||
const { expiresAfterMins } = session
|
await this.deleteSession(session)
|
||||||
|
|
||||||
// delay session destroy if expiresAfterMins present
|
|
||||||
if (expiresAfterMins && session.state !== SessionState.completed) {
|
|
||||||
// calculate session death time using expiresAfterMins
|
|
||||||
const newDeathTimeStamp =
|
|
||||||
parseInt(session.deathTimeStamp) +
|
|
||||||
expiresAfterMins.mins * 60 * 1000
|
|
||||||
session.deathTimeStamp = newDeathTimeStamp.toString()
|
|
||||||
|
|
||||||
// set expiresAfterMins to true to avoid using it again
|
|
||||||
session.expiresAfterMins!.used = true
|
|
||||||
|
|
||||||
this.scheduleSessionDestroy(session)
|
|
||||||
} else {
|
|
||||||
await this.deleteSession(session)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parseInt(session.deathTimeStamp) - new Date().getTime() - 100
|
parseInt(session.deathTimeStamp) - new Date().getTime() - 100
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const createJSProgram = async (
|
|||||||
) => {
|
) => {
|
||||||
const varStatments = Object.keys(vars).reduce(
|
const varStatments = Object.keys(vars).reduce(
|
||||||
(computed: string, key: string) =>
|
(computed: string, key: string) =>
|
||||||
`${computed}const ${key} = \`${vars[key]}\`;\n`,
|
`${computed}const ${key} = '${vars[key]}';\n`,
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { WriteStream, createWriteStream } from 'fs'
|
|||||||
import { execFile } from 'child_process'
|
import { execFile } from 'child_process'
|
||||||
import { once } from 'stream'
|
import { once } from 'stream'
|
||||||
import { createFile, moveFile } from '@sasjs/utils'
|
import { createFile, moveFile } from '@sasjs/utils'
|
||||||
import { PreProgramVars, Session, SessionState } from '../../types'
|
import { PreProgramVars, Session } from '../../types'
|
||||||
import { RunTimeType } from '../../utils'
|
import { RunTimeType } from '../../utils'
|
||||||
import {
|
import {
|
||||||
ExecutionVars,
|
ExecutionVars,
|
||||||
@@ -49,7 +49,7 @@ export const processProgram = async (
|
|||||||
await moveFile(codePath + '.bkp', codePath)
|
await moveFile(codePath + '.bkp', codePath)
|
||||||
|
|
||||||
// we now need to poll the session status
|
// we now need to poll the session status
|
||||||
while (session.state !== SessionState.completed) {
|
while (!session.completed) {
|
||||||
await delay(50)
|
await delay(50)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -114,20 +114,13 @@ export const processProgram = async (
|
|||||||
|
|
||||||
await execFilePromise(executablePath, [codePath], writeStream)
|
await execFilePromise(executablePath, [codePath], writeStream)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
session.state = SessionState.completed
|
session.completed = true
|
||||||
|
|
||||||
process.logger.info('session completed', session)
|
process.logger.info('session completed', session)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
session.state = SessionState.failed
|
session.completed = true
|
||||||
|
session.crashed = err.toString()
|
||||||
session.failureReason = err.toString()
|
process.logger.error('session crashed', session.id, session.crashed)
|
||||||
|
|
||||||
process.logger.error(
|
|
||||||
'session crashed',
|
|
||||||
session.id,
|
|
||||||
session.failureReason
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// copy the code file to log and end write stream
|
// copy the code file to log and end write stream
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ interface RegisterPermissionPayload {
|
|||||||
principalType: PrincipalType
|
principalType: PrincipalType
|
||||||
/**
|
/**
|
||||||
* The id of user or group to which a rule is assigned.
|
* The id of user or group to which a rule is assigned.
|
||||||
* @example 'groupIdString'
|
* @example 123
|
||||||
*/
|
*/
|
||||||
principalId: string
|
principalId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdatePermissionPayload {
|
interface UpdatePermissionPayload {
|
||||||
@@ -70,7 +70,7 @@ interface UpdatePermissionPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PermissionDetailsResponse {
|
export interface PermissionDetailsResponse {
|
||||||
uid: string
|
permissionId: number
|
||||||
path: string
|
path: string
|
||||||
type: string
|
type: string
|
||||||
setting: string
|
setting: string
|
||||||
@@ -91,24 +91,24 @@ export class PermissionController {
|
|||||||
*/
|
*/
|
||||||
@Example<PermissionDetailsResponse[]>([
|
@Example<PermissionDetailsResponse[]>([
|
||||||
{
|
{
|
||||||
uid: 'permissionId1String',
|
permissionId: 123,
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
user: {
|
user: {
|
||||||
uid: 'user1-id',
|
id: 1,
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uid: 'permissionId2String',
|
permissionId: 124,
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
group: {
|
group: {
|
||||||
uid: 'group1-id',
|
groupId: 1,
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users',
|
description: 'This group represents Data Controller Users',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
@@ -128,12 +128,12 @@ export class PermissionController {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<PermissionDetailsResponse>({
|
@Example<PermissionDetailsResponse>({
|
||||||
uid: 'permissionIdString',
|
permissionId: 123,
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
user: {
|
user: {
|
||||||
uid: 'userIdString',
|
id: 1,
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
@@ -149,36 +149,36 @@ export class PermissionController {
|
|||||||
/**
|
/**
|
||||||
* @summary Update permission setting. Admin only
|
* @summary Update permission setting. Admin only
|
||||||
* @param permissionId The permission's identifier
|
* @param permissionId The permission's identifier
|
||||||
* @example permissionId "permissionIdString"
|
* @example permissionId 1234
|
||||||
*/
|
*/
|
||||||
@Example<PermissionDetailsResponse>({
|
@Example<PermissionDetailsResponse>({
|
||||||
uid: 'permissionIdString',
|
permissionId: 123,
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
user: {
|
user: {
|
||||||
uid: 'userIdString',
|
id: 1,
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@Patch('{uid}')
|
@Patch('{permissionId}')
|
||||||
public async updatePermission(
|
public async updatePermission(
|
||||||
@Path() uid: string,
|
@Path() permissionId: number,
|
||||||
@Body() body: UpdatePermissionPayload
|
@Body() body: UpdatePermissionPayload
|
||||||
): Promise<PermissionDetailsResponse> {
|
): Promise<PermissionDetailsResponse> {
|
||||||
return updatePermission(uid, body)
|
return updatePermission(permissionId, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Delete a permission. Admin only.
|
* @summary Delete a permission. Admin only.
|
||||||
* @param permissionId The user's identifier
|
* @param permissionId The user's identifier
|
||||||
* @example permissionId "permissionIdString"
|
* @example permissionId 1234
|
||||||
*/
|
*/
|
||||||
@Delete('{uid}')
|
@Delete('{permissionId}')
|
||||||
public async deletePermission(@Path() uid: string) {
|
public async deletePermission(@Path() permissionId: number) {
|
||||||
return deletePermission(uid)
|
return deletePermission(permissionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ const getAllPermissions = async (
|
|||||||
else {
|
else {
|
||||||
const permissions: PermissionDetailsResponse[] = []
|
const permissions: PermissionDetailsResponse[] = []
|
||||||
|
|
||||||
const dbUser = await User.findOne({ _id: user?.userId })
|
const dbUser = await User.findOne({ id: user?.userId })
|
||||||
if (!dbUser)
|
if (!dbUser)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -227,7 +227,7 @@ const createPermission = async ({
|
|||||||
|
|
||||||
switch (principalType) {
|
switch (principalType) {
|
||||||
case PrincipalType.user: {
|
case PrincipalType.user: {
|
||||||
const userInDB = await User.findOne({ _id: principalId })
|
const userInDB = await User.findOne({ id: principalId })
|
||||||
if (!userInDB)
|
if (!userInDB)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -259,7 +259,7 @@ const createPermission = async ({
|
|||||||
permission.user = userInDB._id
|
permission.user = userInDB._id
|
||||||
|
|
||||||
user = {
|
user = {
|
||||||
uid: userInDB.uid,
|
id: userInDB.id,
|
||||||
username: userInDB.username,
|
username: userInDB.username,
|
||||||
displayName: userInDB.displayName,
|
displayName: userInDB.displayName,
|
||||||
isAdmin: userInDB.isAdmin
|
isAdmin: userInDB.isAdmin
|
||||||
@@ -267,7 +267,7 @@ const createPermission = async ({
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case PrincipalType.group: {
|
case PrincipalType.group: {
|
||||||
const groupInDB = await Group.findOne({ _id: principalId })
|
const groupInDB = await Group.findOne({ groupId: principalId })
|
||||||
if (!groupInDB)
|
if (!groupInDB)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -291,13 +291,13 @@ const createPermission = async ({
|
|||||||
permission.group = groupInDB._id
|
permission.group = groupInDB._id
|
||||||
|
|
||||||
group = {
|
group = {
|
||||||
uid: groupInDB.uid,
|
groupId: groupInDB.groupId,
|
||||||
name: groupInDB.name,
|
name: groupInDB.name,
|
||||||
description: groupInDB.description,
|
description: groupInDB.description,
|
||||||
isActive: groupInDB.isActive,
|
isActive: groupInDB.isActive,
|
||||||
users: groupInDB.populate({
|
users: groupInDB.populate({
|
||||||
path: 'users',
|
path: 'users',
|
||||||
select: 'uid username displayName isAdmin -_id',
|
select: 'id username displayName isAdmin -_id',
|
||||||
options: { limit: 15 }
|
options: { limit: 15 }
|
||||||
}) as unknown as UserResponse[]
|
}) as unknown as UserResponse[]
|
||||||
}
|
}
|
||||||
@@ -314,7 +314,7 @@ const createPermission = async ({
|
|||||||
const savedPermission = await permission.save()
|
const savedPermission = await permission.save()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: savedPermission.uid,
|
permissionId: savedPermission.permissionId,
|
||||||
path: savedPermission.path,
|
path: savedPermission.path,
|
||||||
type: savedPermission.type,
|
type: savedPermission.type,
|
||||||
setting: savedPermission.setting,
|
setting: savedPermission.setting,
|
||||||
@@ -324,21 +324,27 @@ const createPermission = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updatePermission = async (
|
const updatePermission = async (
|
||||||
uid: string,
|
id: number,
|
||||||
data: UpdatePermissionPayload
|
data: UpdatePermissionPayload
|
||||||
): Promise<PermissionDetailsResponse> => {
|
): Promise<PermissionDetailsResponse> => {
|
||||||
const { setting } = data
|
const { setting } = data
|
||||||
|
|
||||||
const updatedPermission = (await Permission.findOneAndUpdate(
|
const updatedPermission = (await Permission.findOneAndUpdate(
|
||||||
{ _id: uid },
|
{ permissionId: id },
|
||||||
{ setting },
|
{ setting },
|
||||||
{ new: true }
|
{ new: true }
|
||||||
)
|
)
|
||||||
.select('uid path type setting')
|
.select({
|
||||||
.populate({ path: 'user', select: 'uid username displayName isAdmin' })
|
_id: 0,
|
||||||
|
permissionId: 1,
|
||||||
|
path: 1,
|
||||||
|
type: 1,
|
||||||
|
setting: 1
|
||||||
|
})
|
||||||
|
.populate({ path: 'user', select: 'id username displayName isAdmin -_id' })
|
||||||
.populate({
|
.populate({
|
||||||
path: 'group',
|
path: 'group',
|
||||||
select: 'groupId name description'
|
select: 'groupId name description -_id'
|
||||||
})) as unknown as PermissionDetailsResponse
|
})) as unknown as PermissionDetailsResponse
|
||||||
if (!updatedPermission)
|
if (!updatedPermission)
|
||||||
throw {
|
throw {
|
||||||
@@ -350,13 +356,13 @@ const updatePermission = async (
|
|||||||
return updatedPermission
|
return updatedPermission
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletePermission = async (uid: string) => {
|
const deletePermission = async (id: number) => {
|
||||||
const permission = await Permission.findOne({ _id: uid })
|
const permission = await Permission.findOne({ permissionId: id })
|
||||||
if (!permission)
|
if (!permission)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
status: 'Not Found',
|
status: 'Not Found',
|
||||||
message: 'Permission not found.'
|
message: 'Permission not found.'
|
||||||
}
|
}
|
||||||
await Permission.deleteOne({ _id: uid })
|
await Permission.deleteOne({ permissionId: id })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { Request, Security, Route, Tags, Example, Get } from 'tsoa'
|
import { Request, Security, Route, Tags, Example, Get } from 'tsoa'
|
||||||
import { UserResponse } from './user'
|
import { UserResponse } from './user'
|
||||||
import { getSessionController } from './internal'
|
|
||||||
import { SessionState } from '../types'
|
|
||||||
|
|
||||||
interface SessionResponse extends Omit<UserResponse, 'uid'> {
|
interface SessionResponse extends UserResponse {
|
||||||
id: string
|
needsToUpdatePassword: boolean
|
||||||
needsToUpdatePassword?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@@ -17,12 +14,11 @@ export class SessionController {
|
|||||||
* @summary Get session info (username).
|
* @summary Get session info (username).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<SessionResponse>({
|
@Example<UserResponse>({
|
||||||
id: 'userIdString',
|
id: 123,
|
||||||
username: 'johnusername',
|
username: 'johnusername',
|
||||||
displayName: 'John',
|
displayName: 'John',
|
||||||
isAdmin: false,
|
isAdmin: false
|
||||||
needsToUpdatePassword: false
|
|
||||||
})
|
})
|
||||||
@Get('/')
|
@Get('/')
|
||||||
public async session(
|
public async session(
|
||||||
@@ -30,18 +26,6 @@ export class SessionController {
|
|||||||
): Promise<SessionResponse> {
|
): Promise<SessionResponse> {
|
||||||
return session(request)
|
return session(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The polling endpoint is currently implemented for single-server deployments only.<br>
|
|
||||||
* Load balanced / grid topologies will be supported in a future release.<br>
|
|
||||||
* If your site requires this, please reach out to SASjs Support.
|
|
||||||
* @summary Get session state (initialising, pending, running, completed, failed).
|
|
||||||
* @example completed
|
|
||||||
*/
|
|
||||||
@Get('/:sessionId/state')
|
|
||||||
public async sessionState(sessionId: string): Promise<SessionState> {
|
|
||||||
return sessionState(sessionId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = (req: express.Request) => ({
|
const session = (req: express.Request) => ({
|
||||||
@@ -51,23 +35,3 @@ const session = (req: express.Request) => ({
|
|||||||
isAdmin: req.user!.isAdmin,
|
isAdmin: req.user!.isAdmin,
|
||||||
needsToUpdatePassword: req.user!.needsToUpdatePassword
|
needsToUpdatePassword: req.user!.needsToUpdatePassword
|
||||||
})
|
})
|
||||||
|
|
||||||
const sessionState = (sessionId: string): SessionState => {
|
|
||||||
for (let runTime of process.runTimes) {
|
|
||||||
// get session controller for each available runTime
|
|
||||||
const sessionController = getSessionController(runTime)
|
|
||||||
|
|
||||||
// get session by sessionId
|
|
||||||
const session = sessionController.getSessionById(sessionId)
|
|
||||||
|
|
||||||
// return session state if session was found
|
|
||||||
if (session) {
|
|
||||||
return session.state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw {
|
|
||||||
code: 404,
|
|
||||||
message: `Session with ID '${sessionId}' was not found.`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { Request, Security, Route, Tags, Post, Body, Get, Query } from 'tsoa'
|
import { Request, Security, Route, Tags, Post, Body, Get, Query } from 'tsoa'
|
||||||
import {
|
import { ExecutionController, ExecutionVars } from './internal'
|
||||||
ExecutionController,
|
|
||||||
ExecutionVars,
|
|
||||||
getSessionController
|
|
||||||
} from './internal'
|
|
||||||
import {
|
import {
|
||||||
getPreProgramVariables,
|
getPreProgramVariables,
|
||||||
makeFilesNamesMap,
|
makeFilesNamesMap,
|
||||||
getRunTimeAndFilePath
|
getRunTimeAndFilePath
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { MulterFile } from '../types/Upload'
|
import { MulterFile } from '../types/Upload'
|
||||||
|
import { debug } from 'console'
|
||||||
|
|
||||||
interface ExecutePostRequestPayload {
|
interface ExecutePostRequestPayload {
|
||||||
/**
|
/**
|
||||||
@@ -20,36 +17,6 @@ interface ExecutePostRequestPayload {
|
|||||||
_program?: string
|
_program?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TriggerProgramPayload {
|
|
||||||
/**
|
|
||||||
* Location of SAS program.
|
|
||||||
* @example "/Public/somefolder/some.file"
|
|
||||||
*/
|
|
||||||
_program: string
|
|
||||||
/**
|
|
||||||
* Amount of minutes after the completion of the program when the session must be
|
|
||||||
* destroyed.
|
|
||||||
* @example 15
|
|
||||||
*/
|
|
||||||
expiresAfterMins?: number
|
|
||||||
/**
|
|
||||||
* Query param for setting debug mode.
|
|
||||||
*/
|
|
||||||
_debug?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TriggerProgramResponse {
|
|
||||||
/**
|
|
||||||
* `sessionId` is the ID of the session and the name of the temporary folder
|
|
||||||
* used to store program outputs.<br><br>
|
|
||||||
* For SAS, this would be the location of the SASWORK folder.<br><br>
|
|
||||||
* `sessionId` can be used to poll session state using the
|
|
||||||
* GET /SASjsApi/session/{sessionId}/state endpoint.
|
|
||||||
* @example "20241028074744-54132-1730101664824"
|
|
||||||
*/
|
|
||||||
sessionId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/stp')
|
@Route('SASjsApi/stp')
|
||||||
@Tags('STP')
|
@Tags('STP')
|
||||||
@@ -63,8 +30,8 @@ export class STPController {
|
|||||||
* https://server.sasjs.io/storedprograms
|
* https://server.sasjs.io/storedprograms
|
||||||
*
|
*
|
||||||
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||||
* @param _program Location of Stored Program in SASjs Drive.
|
* @param _program Location of code in SASjs Drive
|
||||||
* @param _debug Optional query param for setting debug mode (returns the session log in the response body).
|
* @param _debug Optional query param for setting debug mode, which will return the session log.
|
||||||
* @example _program "/Projects/myApp/some/program"
|
* @example _program "/Projects/myApp/some/program"
|
||||||
* @example _debug 131
|
* @example _debug 131
|
||||||
*/
|
*/
|
||||||
@@ -112,26 +79,6 @@ export class STPController {
|
|||||||
|
|
||||||
return execute(request, program!, vars, otherArgs)
|
return execute(request, program!, vars, otherArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger Program on the Specified Runtime.
|
|
||||||
* @summary Triggers program and returns SessionId immediately - does not wait for program completion.
|
|
||||||
* @param _program Location of code in SASjs Drive.
|
|
||||||
* @param expiresAfterMins Optional query param for setting amount of minutes after the completion of the program when the session must be destroyed.
|
|
||||||
* @param _debug Optional query param for setting debug mode.
|
|
||||||
* @example _program "/Projects/myApp/some/program"
|
|
||||||
* @example _debug 131
|
|
||||||
* @example expiresAfterMins 15
|
|
||||||
*/
|
|
||||||
@Post('/trigger')
|
|
||||||
public async triggerProgram(
|
|
||||||
@Request() request: express.Request,
|
|
||||||
@Query() _program: string,
|
|
||||||
@Query() _debug?: number,
|
|
||||||
@Query() expiresAfterMins?: number
|
|
||||||
): Promise<TriggerProgramResponse> {
|
|
||||||
return triggerProgram(request, { _program, _debug, expiresAfterMins })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const execute = async (
|
const execute = async (
|
||||||
@@ -170,52 +117,3 @@ const execute = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerProgram = async (
|
|
||||||
req: express.Request,
|
|
||||||
{ _program, _debug, expiresAfterMins }: TriggerProgramPayload
|
|
||||||
): Promise<TriggerProgramResponse> => {
|
|
||||||
try {
|
|
||||||
// put _program query param into vars object
|
|
||||||
const vars: { [key: string]: string | number } = { _program }
|
|
||||||
|
|
||||||
// if present add _debug query param to vars object
|
|
||||||
if (_debug) {
|
|
||||||
vars._debug = _debug
|
|
||||||
}
|
|
||||||
|
|
||||||
// get code path and runTime
|
|
||||||
const { codePath, runTime } = await getRunTimeAndFilePath(_program)
|
|
||||||
|
|
||||||
// get session controller based on runTime
|
|
||||||
const sessionController = getSessionController(runTime)
|
|
||||||
|
|
||||||
// get session
|
|
||||||
const session = await sessionController.getSession()
|
|
||||||
|
|
||||||
// add expiresAfterMins to session if provided
|
|
||||||
if (expiresAfterMins) {
|
|
||||||
// expiresAfterMins.used is set initially to false
|
|
||||||
session.expiresAfterMins = { mins: expiresAfterMins, used: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
// call executeFile method of ExecutionController without awaiting
|
|
||||||
new ExecutionController().executeFile({
|
|
||||||
programPath: codePath,
|
|
||||||
runTime,
|
|
||||||
preProgramVariables: getPreProgramVariables(req),
|
|
||||||
vars,
|
|
||||||
session
|
|
||||||
})
|
|
||||||
|
|
||||||
// return session id
|
|
||||||
return { sessionId: session.id }
|
|
||||||
} catch (err: any) {
|
|
||||||
throw {
|
|
||||||
code: 400,
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Job execution failed.',
|
|
||||||
error: typeof err === 'object' ? err.toString() : err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,14 +26,18 @@ import {
|
|||||||
import { GroupController, GroupResponse } from './group'
|
import { GroupController, GroupResponse } from './group'
|
||||||
|
|
||||||
export interface UserResponse {
|
export interface UserResponse {
|
||||||
uid: string
|
id: number
|
||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserDetailsResponse extends UserResponse {
|
export interface UserDetailsResponse {
|
||||||
|
id: number
|
||||||
|
displayName: string
|
||||||
|
username: string
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
|
isAdmin: boolean
|
||||||
autoExec?: string
|
autoExec?: string
|
||||||
groups?: GroupResponse[]
|
groups?: GroupResponse[]
|
||||||
}
|
}
|
||||||
@@ -48,13 +52,13 @@ export class UserController {
|
|||||||
*/
|
*/
|
||||||
@Example<UserResponse[]>([
|
@Example<UserResponse[]>([
|
||||||
{
|
{
|
||||||
uid: 'userIdString',
|
id: 123,
|
||||||
username: 'johnusername',
|
username: 'johnusername',
|
||||||
displayName: 'John',
|
displayName: 'John',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uid: 'anotherUserIdString',
|
id: 456,
|
||||||
username: 'starkusername',
|
username: 'starkusername',
|
||||||
displayName: 'Stark',
|
displayName: 'Stark',
|
||||||
isAdmin: true
|
isAdmin: true
|
||||||
@@ -70,7 +74,7 @@ export class UserController {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<UserDetailsResponse>({
|
@Example<UserDetailsResponse>({
|
||||||
uid: 'userIdString',
|
id: 1234,
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
@@ -107,20 +111,20 @@ export class UserController {
|
|||||||
* Only Admin or user itself will get user autoExec code.
|
* Only Admin or user itself will get user autoExec code.
|
||||||
* @summary Get user properties - such as group memberships, userName, displayName.
|
* @summary Get user properties - such as group memberships, userName, displayName.
|
||||||
* @param userId The user's identifier
|
* @param userId The user's identifier
|
||||||
* @example userId "userIdString"
|
* @example userId 1234
|
||||||
*/
|
*/
|
||||||
@Get('{uid}')
|
@Get('{userId}')
|
||||||
public async getUser(
|
public async getUser(
|
||||||
@Request() req: express.Request,
|
@Request() req: express.Request,
|
||||||
@Path() uid: string
|
@Path() userId: number
|
||||||
): Promise<UserDetailsResponse> {
|
): Promise<UserDetailsResponse> {
|
||||||
const { MODE } = process.env
|
const { MODE } = process.env
|
||||||
|
|
||||||
if (MODE === ModeType.Desktop) return getDesktopAutoExec()
|
if (MODE === ModeType.Desktop) return getDesktopAutoExec()
|
||||||
|
|
||||||
const { user } = req
|
const { user } = req
|
||||||
const getAutoExec = user!.isAdmin || user!.userId === uid
|
const getAutoExec = user!.isAdmin || user!.userId == userId
|
||||||
return getUser({ _id: uid }, getAutoExec)
|
return getUser({ id: userId }, getAutoExec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,7 +133,7 @@ export class UserController {
|
|||||||
* @example username "johnSnow01"
|
* @example username "johnSnow01"
|
||||||
*/
|
*/
|
||||||
@Example<UserDetailsResponse>({
|
@Example<UserDetailsResponse>({
|
||||||
uid: 'userIdString',
|
id: 1234,
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
@@ -154,7 +158,7 @@ export class UserController {
|
|||||||
* @example userId "1234"
|
* @example userId "1234"
|
||||||
*/
|
*/
|
||||||
@Example<UserDetailsResponse>({
|
@Example<UserDetailsResponse>({
|
||||||
uid: 'userIdString',
|
id: 1234,
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
@@ -162,7 +166,7 @@ export class UserController {
|
|||||||
})
|
})
|
||||||
@Patch('{userId}')
|
@Patch('{userId}')
|
||||||
public async updateUser(
|
public async updateUser(
|
||||||
@Path() userId: string,
|
@Path() userId: number,
|
||||||
@Body() body: UserPayload
|
@Body() body: UserPayload
|
||||||
): Promise<UserDetailsResponse> {
|
): Promise<UserDetailsResponse> {
|
||||||
const { MODE } = process.env
|
const { MODE } = process.env
|
||||||
@@ -170,7 +174,7 @@ export class UserController {
|
|||||||
if (MODE === ModeType.Desktop)
|
if (MODE === ModeType.Desktop)
|
||||||
return updateDesktopAutoExec(body.autoExec ?? '')
|
return updateDesktopAutoExec(body.autoExec ?? '')
|
||||||
|
|
||||||
return updateUser({ _id: userId }, body)
|
return updateUser({ id: userId }, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,16 +198,18 @@ export class UserController {
|
|||||||
*/
|
*/
|
||||||
@Delete('{userId}')
|
@Delete('{userId}')
|
||||||
public async deleteUser(
|
public async deleteUser(
|
||||||
@Path() userId: string,
|
@Path() userId: number,
|
||||||
@Body() body: { password?: string },
|
@Body() body: { password?: string },
|
||||||
@Query() @Hidden() isAdmin: boolean = false
|
@Query() @Hidden() isAdmin: boolean = false
|
||||||
) {
|
) {
|
||||||
return deleteUser({ _id: userId }, isAdmin, body)
|
return deleteUser({ id: userId }, isAdmin, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllUsers = async (): Promise<UserResponse[]> =>
|
const getAllUsers = async (): Promise<UserResponse[]> =>
|
||||||
await User.find({}).select('uid username displayName isAdmin').exec()
|
await User.find({})
|
||||||
|
.select({ _id: 0, id: 1, username: 1, displayName: 1, isAdmin: 1 })
|
||||||
|
.exec()
|
||||||
|
|
||||||
const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
||||||
const { displayName, username, password, isAdmin, isActive, autoExec } = data
|
const { displayName, username, password, isAdmin, isActive, autoExec } = data
|
||||||
@@ -233,15 +239,15 @@ const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
|||||||
|
|
||||||
const groupController = new GroupController()
|
const groupController = new GroupController()
|
||||||
const allUsersGroup = await groupController
|
const allUsersGroup = await groupController
|
||||||
.getGroupByName(ALL_USERS_GROUP.name)
|
.getGroupByGroupName(ALL_USERS_GROUP.name)
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
|
|
||||||
if (allUsersGroup) {
|
if (allUsersGroup) {
|
||||||
await groupController.addUserToGroup(allUsersGroup.uid, savedUser.uid)
|
await groupController.addUserToGroup(allUsersGroup.groupId, savedUser.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: savedUser.uid,
|
id: savedUser.id,
|
||||||
displayName: savedUser.displayName,
|
displayName: savedUser.displayName,
|
||||||
username: savedUser.username,
|
username: savedUser.username,
|
||||||
isActive: savedUser.isActive,
|
isActive: savedUser.isActive,
|
||||||
@@ -250,8 +256,8 @@ const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetUserBy {
|
interface GetUserBy {
|
||||||
_id?: string
|
id?: number
|
||||||
username?: string
|
username?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,10 +267,10 @@ const getUser = async (
|
|||||||
): Promise<UserDetailsResponse> => {
|
): Promise<UserDetailsResponse> => {
|
||||||
const user = (await User.findOne(
|
const user = (await User.findOne(
|
||||||
findBy,
|
findBy,
|
||||||
`uid displayName username isActive isAdmin autoExec`
|
`id displayName username isActive isAdmin autoExec -_id`
|
||||||
).populate(
|
).populate(
|
||||||
'groups',
|
'groups',
|
||||||
'uid name description'
|
'groupId name description -_id'
|
||||||
)) as unknown as UserDetailsResponse
|
)) as unknown as UserDetailsResponse
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
@@ -274,12 +280,12 @@ const getUser = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: user.uid,
|
id: user.id,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
isActive: user.isActive,
|
isActive: user.isActive,
|
||||||
isAdmin: user.isAdmin,
|
isAdmin: user.isAdmin,
|
||||||
autoExec: getAutoExec ? (user.autoExec ?? '') : undefined,
|
autoExec: getAutoExec ? user.autoExec ?? '' : undefined,
|
||||||
groups: user.groups
|
groups: user.groups
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,7 +293,7 @@ const getUser = async (
|
|||||||
const getDesktopAutoExec = async () => {
|
const getDesktopAutoExec = async () => {
|
||||||
return {
|
return {
|
||||||
...desktopUser,
|
...desktopUser,
|
||||||
uid: desktopUser.userId,
|
id: desktopUser.userId,
|
||||||
autoExec: await getUserAutoExec()
|
autoExec: await getUserAutoExec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,8 +329,8 @@ const updateUser = async (
|
|||||||
const usernameExist = await User.findOne({ username })
|
const usernameExist = await User.findOne({ username })
|
||||||
if (usernameExist) {
|
if (usernameExist) {
|
||||||
if (
|
if (
|
||||||
(findBy._id && usernameExist.uid !== findBy._id) ||
|
(findBy.id && usernameExist.id != findBy.id) ||
|
||||||
(findBy.username && usernameExist.username !== findBy.username)
|
(findBy.username && usernameExist.username != findBy.username)
|
||||||
)
|
)
|
||||||
throw {
|
throw {
|
||||||
code: 409,
|
code: 409,
|
||||||
@@ -344,11 +350,11 @@ const updateUser = async (
|
|||||||
if (!updatedUser)
|
if (!updatedUser)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
message: `Unable to find user with ${findBy._id || findBy.username}`
|
message: `Unable to find user with ${findBy.id || findBy.username}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: updatedUser.uid,
|
id: updatedUser.id,
|
||||||
username: updatedUser.username,
|
username: updatedUser.username,
|
||||||
displayName: updatedUser.displayName,
|
displayName: updatedUser.displayName,
|
||||||
isAdmin: updatedUser.isAdmin,
|
isAdmin: updatedUser.isAdmin,
|
||||||
@@ -361,7 +367,7 @@ const updateDesktopAutoExec = async (autoExec: string) => {
|
|||||||
await updateUserAutoExec(autoExec)
|
await updateUserAutoExec(autoExec)
|
||||||
return {
|
return {
|
||||||
...desktopUser,
|
...desktopUser,
|
||||||
uid: desktopUser.userId,
|
id: desktopUser.userId,
|
||||||
autoExec
|
autoExec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const authenticateToken = async (
|
|||||||
const { MODE } = process.env
|
const { MODE } = process.env
|
||||||
if (MODE === ModeType.Desktop) {
|
if (MODE === ModeType.Desktop) {
|
||||||
req.user = {
|
req.user = {
|
||||||
userId: '1234',
|
userId: 1234,
|
||||||
clientId: 'desktopModeClientId',
|
clientId: 'desktopModeClientId',
|
||||||
username: 'desktopModeUsername',
|
username: 'desktopModeUsername',
|
||||||
displayName: 'desktopModeDisplayName',
|
displayName: 'desktopModeDisplayName',
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const authorize: RequestHandler = async (req, res, next) => {
|
|||||||
// no need to check for permissions when route is Public
|
// no need to check for permissions when route is Public
|
||||||
if (await isPublicRoute(req)) return next()
|
if (await isPublicRoute(req)) return next()
|
||||||
|
|
||||||
const dbUser = await User.findOne({ _id: user.userId })
|
const dbUser = await User.findOne({ id: user.userId })
|
||||||
if (!dbUser) return res.sendStatus(401)
|
if (!dbUser) return res.sendStatus(401)
|
||||||
|
|
||||||
const path = getPath(req)
|
const path = getPath(req)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const desktopRestrict: RequestHandler = (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const desktopUser: RequestUser = {
|
export const desktopUser: RequestUser = {
|
||||||
userId: '12345',
|
userId: 12345,
|
||||||
clientId: 'desktop_app',
|
clientId: 'desktop_app',
|
||||||
username: userInfo().username,
|
username: userInfo().username,
|
||||||
displayName: userInfo().username,
|
displayName: userInfo().username,
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export const verifyAdminIfNeeded: RequestHandler = (req, res, next) => {
|
|||||||
if (!user?.isAdmin) {
|
if (!user?.isAdmin) {
|
||||||
let adminAccountRequired: boolean = true
|
let adminAccountRequired: boolean = true
|
||||||
|
|
||||||
if (req.params.uid) {
|
if (req.params.userId) {
|
||||||
adminAccountRequired = user?.userId !== req.params.uid
|
adminAccountRequired = user?.userId !== parseInt(req.params.userId)
|
||||||
} else if (req.params.username) {
|
} else if (req.params.username) {
|
||||||
adminAccountRequired = user?.username !== req.params.username
|
adminAccountRequired = user?.username !== req.params.username
|
||||||
}
|
}
|
||||||
|
|||||||
15
api/src/model/Counter.ts
Normal file
15
api/src/model/Counter.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import mongoose, { Schema } from 'mongoose'
|
||||||
|
|
||||||
|
const CounterSchema = new Schema({
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
seq: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default mongoose.model('Counter', CounterSchema)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Schema, model, Document, Model } from 'mongoose'
|
import { Schema, model, Document, Model } from 'mongoose'
|
||||||
import { GroupDetailsResponse } from '../controllers'
|
import { GroupDetailsResponse } from '../controllers'
|
||||||
import User, { IUser } from './User'
|
import User, { IUser } from './User'
|
||||||
import { AuthProviderType } from '../utils'
|
import { AuthProviderType, getSequenceNextValue } from '../utils'
|
||||||
|
|
||||||
export const PUBLIC_GROUP_NAME = 'public'
|
export const PUBLIC_GROUP_NAME = 'Public'
|
||||||
|
|
||||||
export interface GroupPayload {
|
export interface GroupPayload {
|
||||||
/**
|
/**
|
||||||
@@ -24,12 +24,10 @@ export interface GroupPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IGroupDocument extends GroupPayload, Document {
|
interface IGroupDocument extends GroupPayload, Document {
|
||||||
|
groupId: number
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
users: Schema.Types.ObjectId[]
|
users: Schema.Types.ObjectId[]
|
||||||
authProvider?: AuthProviderType
|
authProvider?: AuthProviderType
|
||||||
|
|
||||||
// Declare virtual properties as read-only properties
|
|
||||||
readonly uid: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IGroup extends IGroupDocument {
|
interface IGroup extends IGroupDocument {
|
||||||
@@ -39,46 +37,40 @@ interface IGroup extends IGroupDocument {
|
|||||||
}
|
}
|
||||||
interface IGroupModel extends Model<IGroup> {}
|
interface IGroupModel extends Model<IGroup> {}
|
||||||
|
|
||||||
const opts = {
|
const groupSchema = new Schema<IGroupDocument>({
|
||||||
toJSON: {
|
name: {
|
||||||
virtuals: true,
|
type: String,
|
||||||
transform: function (doc: any, ret: any, options: any) {
|
required: true,
|
||||||
delete ret._id
|
unique: true
|
||||||
delete ret.id
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const groupSchema = new Schema<IGroupDocument>(
|
|
||||||
{
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: 'Group description.'
|
|
||||||
},
|
|
||||||
authProvider: {
|
|
||||||
type: String,
|
|
||||||
enum: AuthProviderType
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
|
|
||||||
},
|
},
|
||||||
opts
|
groupId: {
|
||||||
)
|
type: Number,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: 'Group description.'
|
||||||
|
},
|
||||||
|
authProvider: {
|
||||||
|
type: String,
|
||||||
|
enum: AuthProviderType
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
|
||||||
|
})
|
||||||
|
|
||||||
groupSchema.virtual('uid').get(function () {
|
// Hooks
|
||||||
return this._id.toString()
|
groupSchema.pre('save', async function () {
|
||||||
|
if (this.isNew) {
|
||||||
|
this.groupId = await getSequenceNextValue('groupId')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
groupSchema.post('save', function (group: IGroup, next: Function) {
|
groupSchema.post('save', function (group: IGroup, next: Function) {
|
||||||
group.populate('users', 'uid username displayName').then(function () {
|
group.populate('users', 'id username displayName -_id').then(function () {
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Schema, model, Document, Model } from 'mongoose'
|
import { Schema, model, Document, Model } from 'mongoose'
|
||||||
import { PermissionDetailsResponse } from '../controllers'
|
import { PermissionDetailsResponse } from '../controllers'
|
||||||
|
import { getSequenceNextValue } from '../utils'
|
||||||
|
|
||||||
interface GetPermissionBy {
|
interface GetPermissionBy {
|
||||||
user?: Schema.Types.ObjectId
|
user?: Schema.Types.ObjectId
|
||||||
@@ -10,11 +11,9 @@ interface IPermissionDocument extends Document {
|
|||||||
path: string
|
path: string
|
||||||
type: string
|
type: string
|
||||||
setting: string
|
setting: string
|
||||||
|
permissionId: number
|
||||||
user: Schema.Types.ObjectId
|
user: Schema.Types.ObjectId
|
||||||
group: Schema.Types.ObjectId
|
group: Schema.Types.ObjectId
|
||||||
|
|
||||||
// Declare virtual properties as read-only properties
|
|
||||||
readonly uid: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPermission extends IPermissionDocument {}
|
interface IPermission extends IPermissionDocument {}
|
||||||
@@ -23,39 +22,32 @@ interface IPermissionModel extends Model<IPermission> {
|
|||||||
get(getBy: GetPermissionBy): Promise<PermissionDetailsResponse[]>
|
get(getBy: GetPermissionBy): Promise<PermissionDetailsResponse[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts = {
|
const permissionSchema = new Schema<IPermissionDocument>({
|
||||||
toJSON: {
|
permissionId: {
|
||||||
virtuals: true,
|
type: Number,
|
||||||
transform: function (doc: any, ret: any, options: any) {
|
unique: true
|
||||||
delete ret._id
|
|
||||||
delete ret.id
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const permissionSchema = new Schema<IPermissionDocument>(
|
|
||||||
{
|
|
||||||
path: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
setting: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
user: { type: Schema.Types.ObjectId, ref: 'User' },
|
|
||||||
group: { type: Schema.Types.ObjectId, ref: 'Group' }
|
|
||||||
},
|
},
|
||||||
opts
|
path: {
|
||||||
)
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
user: { type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
|
group: { type: Schema.Types.ObjectId, ref: 'Group' }
|
||||||
|
})
|
||||||
|
|
||||||
permissionSchema.virtual('uid').get(function () {
|
// Hooks
|
||||||
return this._id.toString()
|
permissionSchema.pre('save', async function () {
|
||||||
|
if (this.isNew) {
|
||||||
|
this.permissionId = await getSequenceNextValue('permissionId')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Static Methods
|
// Static Methods
|
||||||
@@ -63,14 +55,20 @@ permissionSchema.static('get', async function (getBy: GetPermissionBy): Promise<
|
|||||||
PermissionDetailsResponse[]
|
PermissionDetailsResponse[]
|
||||||
> {
|
> {
|
||||||
return (await this.find(getBy)
|
return (await this.find(getBy)
|
||||||
.select('uid path type setting')
|
.select({
|
||||||
.populate({ path: 'user', select: 'uid username displayName isAdmin' })
|
_id: 0,
|
||||||
|
permissionId: 1,
|
||||||
|
path: 1,
|
||||||
|
type: 1,
|
||||||
|
setting: 1
|
||||||
|
})
|
||||||
|
.populate({ path: 'user', select: 'id username displayName isAdmin -_id' })
|
||||||
.populate({
|
.populate({
|
||||||
path: 'group',
|
path: 'group',
|
||||||
select: 'uid name description',
|
select: 'groupId name description -_id',
|
||||||
populate: {
|
populate: {
|
||||||
path: 'users',
|
path: 'users',
|
||||||
select: 'uid username displayName isAdmin',
|
select: 'id username displayName isAdmin -_id',
|
||||||
options: { limit: 15 }
|
options: { limit: 15 }
|
||||||
}
|
}
|
||||||
})) as unknown as PermissionDetailsResponse[]
|
})) as unknown as PermissionDetailsResponse[]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Schema, model, Document, Model, ObjectId } from 'mongoose'
|
import { Schema, model, Document, Model } from 'mongoose'
|
||||||
import bcrypt from 'bcryptjs'
|
import bcrypt from 'bcryptjs'
|
||||||
import { AuthProviderType } from '../utils'
|
import { AuthProviderType, getSequenceNextValue } from '../utils'
|
||||||
|
|
||||||
export interface UserPayload {
|
export interface UserPayload {
|
||||||
/**
|
/**
|
||||||
@@ -36,6 +36,7 @@ export interface UserPayload {
|
|||||||
|
|
||||||
interface IUserDocument extends UserPayload, Document {
|
interface IUserDocument extends UserPayload, Document {
|
||||||
_id: Schema.Types.ObjectId
|
_id: Schema.Types.ObjectId
|
||||||
|
id: number
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
needsToUpdatePassword: boolean
|
needsToUpdatePassword: boolean
|
||||||
@@ -43,9 +44,6 @@ interface IUserDocument extends UserPayload, Document {
|
|||||||
groups: Schema.Types.ObjectId[]
|
groups: Schema.Types.ObjectId[]
|
||||||
tokens: [{ [key: string]: string }]
|
tokens: [{ [key: string]: string }]
|
||||||
authProvider?: AuthProviderType
|
authProvider?: AuthProviderType
|
||||||
|
|
||||||
// Declare virtual properties as read-only properties
|
|
||||||
readonly uid: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUser extends IUserDocument {
|
export interface IUser extends IUserDocument {
|
||||||
@@ -56,74 +54,70 @@ export interface IUser extends IUserDocument {
|
|||||||
interface IUserModel extends Model<IUser> {
|
interface IUserModel extends Model<IUser> {
|
||||||
hashPassword(password: string): string
|
hashPassword(password: string): string
|
||||||
}
|
}
|
||||||
const opts = {
|
|
||||||
toJSON: {
|
|
||||||
virtuals: true,
|
|
||||||
transform: function (doc: any, ret: any, options: any) {
|
|
||||||
delete ret._id
|
|
||||||
delete ret.id
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userSchema = new Schema<IUserDocument>(
|
const userSchema = new Schema<IUserDocument>({
|
||||||
{
|
displayName: {
|
||||||
displayName: {
|
type: String,
|
||||||
type: String,
|
required: true
|
||||||
required: true
|
|
||||||
},
|
|
||||||
username: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
authProvider: {
|
|
||||||
type: String,
|
|
||||||
enum: AuthProviderType
|
|
||||||
},
|
|
||||||
isAdmin: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
needsToUpdatePassword: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
autoExec: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
|
|
||||||
tokens: [
|
|
||||||
{
|
|
||||||
clientId: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
accessToken: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
refreshToken: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
opts
|
username: {
|
||||||
)
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
authProvider: {
|
||||||
|
type: String,
|
||||||
|
enum: AuthProviderType
|
||||||
|
},
|
||||||
|
isAdmin: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
needsToUpdatePassword: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
autoExec: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
|
||||||
|
tokens: [
|
||||||
|
{
|
||||||
|
clientId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
accessToken: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
refreshToken: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
userSchema.virtual('uid').get(function () {
|
// Hooks
|
||||||
return this._id.toString()
|
userSchema.pre('save', async function (next) {
|
||||||
|
if (this.isNew) {
|
||||||
|
this.id = await getSequenceNextValue('id')
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Static Methods
|
// Static Methods
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { runCodeValidation, triggerCodeValidation } from '../../utils'
|
import { runCodeValidation } from '../../utils'
|
||||||
import { CodeController } from '../../controllers/'
|
import { CodeController } from '../../controllers/'
|
||||||
|
|
||||||
const runRouter = express.Router()
|
const runRouter = express.Router()
|
||||||
@@ -28,22 +28,4 @@ runRouter.post('/execute', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
runRouter.post('/trigger', async (req, res) => {
|
|
||||||
const { error, value: body } = triggerCodeValidation(req.body)
|
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await controller.triggerCode(req, body)
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
const statusCode = err.code
|
|
||||||
|
|
||||||
delete err.code
|
|
||||||
|
|
||||||
res.status(statusCode).send(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default runRouter
|
export default runRouter
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { GroupController } from '../../controllers/'
|
import { GroupController } from '../../controllers/'
|
||||||
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
|
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
|
||||||
import {
|
import { getGroupValidation, registerGroupValidation } from '../../utils'
|
||||||
getGroupValidation,
|
|
||||||
registerGroupValidation,
|
|
||||||
uidValidation
|
|
||||||
} from '../../utils'
|
|
||||||
|
|
||||||
const groupRouter = express.Router()
|
const groupRouter = express.Router()
|
||||||
|
|
||||||
@@ -37,15 +33,12 @@ groupRouter.get('/', authenticateAccessToken, async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
groupRouter.get('/:uid', authenticateAccessToken, async (req, res) => {
|
groupRouter.get('/:groupId', authenticateAccessToken, async (req, res) => {
|
||||||
const { error: uidError, value: params } = uidValidation(req.params)
|
const { groupId } = req.params
|
||||||
if (uidError) return res.status(400).send(uidError.details[0].message)
|
|
||||||
|
|
||||||
const { uid } = params
|
|
||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.getGroup(uid)
|
const response = await controller.getGroup(parseInt(groupId))
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
@@ -63,7 +56,7 @@ groupRouter.get(
|
|||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.getGroupByName(name)
|
const response = await controller.getGroupByGroupName(name)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
@@ -72,15 +65,18 @@ groupRouter.get(
|
|||||||
)
|
)
|
||||||
|
|
||||||
groupRouter.post(
|
groupRouter.post(
|
||||||
'/:groupUid/:userUid',
|
'/:groupId/:userId',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdmin,
|
verifyAdmin,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { groupUid, userUid } = req.params
|
const { groupId, userId } = req.params
|
||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.addUserToGroup(groupUid, userUid)
|
const response = await controller.addUserToGroup(
|
||||||
|
parseInt(groupId),
|
||||||
|
parseInt(userId)
|
||||||
|
)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
@@ -89,15 +85,18 @@ groupRouter.post(
|
|||||||
)
|
)
|
||||||
|
|
||||||
groupRouter.delete(
|
groupRouter.delete(
|
||||||
'/:groupUid/:userUid',
|
'/:groupId/:userId',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdmin,
|
verifyAdmin,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { groupUid, userUid } = req.params
|
const { groupId, userId } = req.params
|
||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.removeUserFromGroup(groupUid, userUid)
|
const response = await controller.removeUserFromGroup(
|
||||||
|
parseInt(groupId),
|
||||||
|
parseInt(userId)
|
||||||
|
)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
@@ -106,18 +105,15 @@ groupRouter.delete(
|
|||||||
)
|
)
|
||||||
|
|
||||||
groupRouter.delete(
|
groupRouter.delete(
|
||||||
'/:uid',
|
'/:groupId',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdmin,
|
verifyAdmin,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { error: uidError, value: params } = uidValidation(req.params)
|
const { groupId } = req.params
|
||||||
if (uidError) return res.status(400).send(uidError.details[0].message)
|
|
||||||
|
|
||||||
const { uid } = params
|
|
||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
await controller.deleteGroup(uid)
|
await controller.deleteGroup(parseInt(groupId))
|
||||||
res.status(200).send('Group Deleted!')
|
res.status(200).send('Group Deleted!')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { PermissionController } from '../../controllers/'
|
|||||||
import { verifyAdmin } from '../../middlewares'
|
import { verifyAdmin } from '../../middlewares'
|
||||||
import {
|
import {
|
||||||
registerPermissionValidation,
|
registerPermissionValidation,
|
||||||
uidValidation,
|
|
||||||
updatePermissionValidation
|
updatePermissionValidation
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
@@ -35,17 +34,14 @@ permissionRouter.post('/', verifyAdmin, async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionRouter.patch('/:uid', verifyAdmin, async (req: any, res) => {
|
permissionRouter.patch('/:permissionId', verifyAdmin, async (req: any, res) => {
|
||||||
const { error: uidError, value: params } = uidValidation(req.params)
|
const { permissionId } = req.params
|
||||||
if (uidError) return res.status(400).send(uidError.details[0].message)
|
|
||||||
|
|
||||||
const { uid } = params
|
|
||||||
|
|
||||||
const { error, value: body } = updatePermissionValidation(req.body)
|
const { error, value: body } = updatePermissionValidation(req.body)
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
if (error) return res.status(400).send(error.details[0].message)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await controller.updatePermission(uid, body)
|
const response = await controller.updatePermission(permissionId, body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const statusCode = err.code
|
const statusCode = err.code
|
||||||
@@ -54,18 +50,20 @@ permissionRouter.patch('/:uid', verifyAdmin, async (req: any, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionRouter.delete('/:uid', verifyAdmin, async (req: any, res) => {
|
permissionRouter.delete(
|
||||||
const { error: uidError, value: params } = uidValidation(req.params)
|
'/:permissionId',
|
||||||
if (uidError) return res.status(400).send(uidError.details[0].message)
|
verifyAdmin,
|
||||||
|
async (req: any, res) => {
|
||||||
|
const { permissionId } = req.params
|
||||||
|
|
||||||
const { uid } = params
|
try {
|
||||||
try {
|
await controller.deletePermission(permissionId)
|
||||||
await controller.deletePermission(uid)
|
res.status(200).send('Permission Deleted!')
|
||||||
res.status(200).send('Permission Deleted!')
|
} catch (err: any) {
|
||||||
} catch (err: any) {
|
const statusCode = err.code
|
||||||
const statusCode = err.code
|
delete err.code
|
||||||
delete err.code
|
res.status(statusCode).send(err.message)
|
||||||
res.status(statusCode).send(err.message)
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
export default permissionRouter
|
export default permissionRouter
|
||||||
|
|||||||
@@ -1,37 +1,16 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { SessionController } from '../../controllers'
|
import { SessionController } from '../../controllers'
|
||||||
import { sessionIdValidation } from '../../utils'
|
|
||||||
|
|
||||||
const sessionRouter = express.Router()
|
const sessionRouter = express.Router()
|
||||||
|
|
||||||
const controller = new SessionController()
|
|
||||||
|
|
||||||
sessionRouter.get('/', async (req, res) => {
|
sessionRouter.get('/', async (req, res) => {
|
||||||
|
const controller = new SessionController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.session(req)
|
const response = await controller.session(req)
|
||||||
|
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(403).send(err.toString())
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
sessionRouter.get('/:sessionId/state', async (req, res) => {
|
|
||||||
const { error, value: params } = sessionIdValidation(req.params)
|
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await controller.sessionState(params.sessionId)
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
const statusCode = err.code
|
|
||||||
|
|
||||||
delete err.code
|
|
||||||
|
|
||||||
res.status(statusCode).send(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default sessionRouter
|
export default sessionRouter
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
generateAccessToken,
|
generateAccessToken,
|
||||||
generateAuthCode,
|
generateAuthCode,
|
||||||
generateRefreshToken,
|
generateRefreshToken,
|
||||||
randomBytesHexString,
|
|
||||||
saveTokensInDB,
|
saveTokensInDB,
|
||||||
verifyTokenInDB
|
verifyTokenInDB
|
||||||
} from '../../../utils'
|
} from '../../../utils'
|
||||||
@@ -21,6 +20,7 @@ import {
|
|||||||
const clientId = 'someclientID'
|
const clientId = 'someclientID'
|
||||||
const clientSecret = 'someclientSecret'
|
const clientSecret = 'someclientSecret'
|
||||||
const user = {
|
const user = {
|
||||||
|
id: 1234,
|
||||||
displayName: 'Test User',
|
displayName: 'Test User',
|
||||||
username: 'testUsername',
|
username: 'testUsername',
|
||||||
password: '87654321',
|
password: '87654321',
|
||||||
@@ -52,7 +52,7 @@ describe('auth', () => {
|
|||||||
describe('token', () => {
|
describe('token', () => {
|
||||||
const userInfo: InfoJWT = {
|
const userInfo: InfoJWT = {
|
||||||
clientId,
|
clientId,
|
||||||
userId: randomBytesHexString(12)
|
userId: user.id
|
||||||
}
|
}
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await userController.createUser(user)
|
await userController.createUser(user)
|
||||||
@@ -151,10 +151,10 @@ describe('auth', () => {
|
|||||||
currentUser = await userController.createUser(user)
|
currentUser = await userController.createUser(user)
|
||||||
refreshToken = generateRefreshToken({
|
refreshToken = generateRefreshToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId: currentUser.uid
|
userId: currentUser.id
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
currentUser.uid,
|
currentUser.id,
|
||||||
clientId,
|
clientId,
|
||||||
'accessToken',
|
'accessToken',
|
||||||
refreshToken
|
refreshToken
|
||||||
@@ -202,11 +202,11 @@ describe('auth', () => {
|
|||||||
currentUser = await userController.createUser(user)
|
currentUser = await userController.createUser(user)
|
||||||
accessToken = generateAccessToken({
|
accessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId: currentUser.uid
|
userId: currentUser.id
|
||||||
})
|
})
|
||||||
|
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
currentUser.uid,
|
currentUser.id,
|
||||||
clientId,
|
clientId,
|
||||||
accessToken,
|
accessToken,
|
||||||
'refreshToken'
|
'refreshToken'
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ describe('client', () => {
|
|||||||
const dbUser = await userController.createUser(adminUser)
|
const dbUser = await userController.createUser(adminUser)
|
||||||
adminAccessToken = generateAccessToken({
|
adminAccessToken = generateAccessToken({
|
||||||
clientId: client.clientId,
|
clientId: client.clientId,
|
||||||
userId: dbUser.uid
|
userId: dbUser.id
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
dbUser.uid,
|
dbUser.id,
|
||||||
client.clientId,
|
client.clientId,
|
||||||
adminAccessToken,
|
adminAccessToken,
|
||||||
'refreshToken'
|
'refreshToken'
|
||||||
@@ -95,10 +95,10 @@ describe('client', () => {
|
|||||||
const dbUser = await userController.createUser(user)
|
const dbUser = await userController.createUser(user)
|
||||||
const accessToken = generateAccessToken({
|
const accessToken = generateAccessToken({
|
||||||
clientId: client.clientId,
|
clientId: client.clientId,
|
||||||
userId: dbUser.uid
|
userId: dbUser.id
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
dbUser.uid,
|
dbUser.id,
|
||||||
client.clientId,
|
client.clientId,
|
||||||
accessToken,
|
accessToken,
|
||||||
'refreshToken'
|
'refreshToken'
|
||||||
@@ -212,10 +212,10 @@ describe('client', () => {
|
|||||||
const dbUser = await userController.createUser(user)
|
const dbUser = await userController.createUser(user)
|
||||||
const accessToken = generateAccessToken({
|
const accessToken = generateAccessToken({
|
||||||
clientId: client.clientId,
|
clientId: client.clientId,
|
||||||
userId: dbUser.uid
|
userId: dbUser.id
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
dbUser.uid,
|
dbUser.id,
|
||||||
client.clientId,
|
client.clientId,
|
||||||
accessToken,
|
accessToken,
|
||||||
'refreshToken'
|
'refreshToken'
|
||||||
|
|||||||
@@ -71,31 +71,31 @@ describe('drive', () => {
|
|||||||
con = await mongoose.connect(mongoServer.getUri())
|
con = await mongoose.connect(mongoServer.getUri())
|
||||||
|
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
accessToken = await generateAndSaveToken(dbUser.uid)
|
accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/deploy',
|
path: '/SASjsApi/drive/deploy',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/deploy/upload',
|
path: '/SASjsApi/drive/deploy/upload',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/file',
|
path: '/SASjsApi/drive/file',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/folder',
|
path: '/SASjsApi/drive/folder',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/rename',
|
path: '/SASjsApi/drive/rename',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1197,7 +1197,7 @@ const getExampleService = (): ServiceMember =>
|
|||||||
((getTreeExample().members[0] as FolderMember).members[0] as FolderMember)
|
((getTreeExample().members[0] as FolderMember).members[0] as FolderMember)
|
||||||
.members[0] as ServiceMember
|
.members[0] as ServiceMember
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: string) => {
|
const generateAndSaveToken = async (userId: number) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from '../../../utils'
|
} from '../../../utils'
|
||||||
import Group, { PUBLIC_GROUP_NAME } from '../../../model/Group'
|
import Group, { PUBLIC_GROUP_NAME } from '../../../model/Group'
|
||||||
import User from '../../../model/User'
|
import User from '../../../model/User'
|
||||||
import { randomBytes } from 'crypto'
|
|
||||||
|
|
||||||
const clientId = 'someclientID'
|
const clientId = 'someclientID'
|
||||||
const adminUser = {
|
const adminUser = {
|
||||||
@@ -76,7 +75,7 @@ describe('group', () => {
|
|||||||
.send(group)
|
.send(group)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
@@ -156,7 +155,7 @@ describe('group', () => {
|
|||||||
const dbGroup = await groupController.createGroup(group)
|
const dbGroup = await groupController.createGroup(group)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -175,17 +174,17 @@ describe('group', () => {
|
|||||||
username: 'deletegroup2'
|
username: 'deletegroup2'
|
||||||
})
|
})
|
||||||
|
|
||||||
await groupController.addUserToGroup(dbGroup.uid, dbUser1.uid)
|
await groupController.addUserToGroup(dbGroup.groupId, dbUser1.id)
|
||||||
await groupController.addUserToGroup(dbGroup.uid, dbUser2.uid)
|
await groupController.addUserToGroup(dbGroup.groupId, dbUser2.id)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
const res1 = await request(app)
|
const res1 = await request(app)
|
||||||
.get(`/SASjsApi/user/${dbUser1.uid}`)
|
.get(`/SASjsApi/user/${dbUser1.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -193,7 +192,7 @@ describe('group', () => {
|
|||||||
expect(res1.body.groups).toEqual([])
|
expect(res1.body.groups).toEqual([])
|
||||||
|
|
||||||
const res2 = await request(app)
|
const res2 = await request(app)
|
||||||
.get(`/SASjsApi/user/${dbUser2.uid}`)
|
.get(`/SASjsApi/user/${dbUser2.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -202,10 +201,8 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Not Found if groupId is incorrect', async () => {
|
it('should respond with Not Found if groupId is incorrect', async () => {
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${hexValue}`)
|
.delete(`/SASjsApi/group/1234`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -232,7 +229,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -248,15 +245,15 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with group', async () => {
|
it('should respond with group', async () => {
|
||||||
const { uid } = await groupController.createGroup(group)
|
const { groupId } = await groupController.createGroup(group)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/group/${uid}`)
|
.get(`/SASjsApi/group/${groupId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
@@ -269,15 +266,15 @@ describe('group', () => {
|
|||||||
username: 'get' + user.username
|
username: 'get' + user.username
|
||||||
})
|
})
|
||||||
|
|
||||||
const { uid } = await groupController.createGroup(group)
|
const { groupId } = await groupController.createGroup(group)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/group/${uid}`)
|
.get(`/SASjsApi/group/${groupId}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
@@ -295,10 +292,8 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Not Found if groupId is incorrect', async () => {
|
it('should respond with Not Found if groupId is incorrect', async () => {
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/group/${hexValue}`)
|
.get('/SASjsApi/group/1234')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -317,7 +312,7 @@ describe('group', () => {
|
|||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
@@ -338,7 +333,7 @@ describe('group', () => {
|
|||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
@@ -384,7 +379,7 @@ describe('group', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
groupId: expect.anything(),
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description
|
description: group.description
|
||||||
}
|
}
|
||||||
@@ -406,7 +401,7 @@ describe('group', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
groupId: expect.anything(),
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description
|
description: group.description
|
||||||
}
|
}
|
||||||
@@ -431,18 +426,18 @@ describe('group', () => {
|
|||||||
const dbUser = await userController.createUser(user)
|
const dbUser = await userController.createUser(user)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
expect(res.body.users).toEqual([
|
expect(res.body.users).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
id: expect.anything(),
|
||||||
username: user.username,
|
username: user.username,
|
||||||
displayName: user.displayName
|
displayName: user.displayName
|
||||||
}
|
}
|
||||||
@@ -457,20 +452,20 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${dbUser.uid}`)
|
.get(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groups).toEqual([
|
expect(res.body.groups).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
groupId: expect.anything(),
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description
|
description: group.description
|
||||||
}
|
}
|
||||||
@@ -483,21 +478,21 @@ describe('group', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'addUserRandomUser'
|
username: 'addUserRandomUser'
|
||||||
})
|
})
|
||||||
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
expect(res.body.users).toEqual([
|
expect(res.body.users).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
id: expect.anything(),
|
||||||
username: 'addUserRandomUser',
|
username: 'addUserRandomUser',
|
||||||
displayName: user.displayName
|
displayName: user.displayName
|
||||||
}
|
}
|
||||||
@@ -531,10 +526,8 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Not Found if groupId is incorrect', async () => {
|
it('should respond with Not Found if groupId is incorrect', async () => {
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${hexValue}/123`)
|
.post('/SASjsApi/group/123/123')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -545,10 +538,8 @@ describe('group', () => {
|
|||||||
|
|
||||||
it('should respond with Not Found if userId is incorrect', async () => {
|
it('should respond with Not Found if userId is incorrect', async () => {
|
||||||
const dbGroup = await groupController.createGroup(group)
|
const dbGroup = await groupController.createGroup(group)
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${hexValue}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/123`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -565,7 +556,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -586,7 +577,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -605,7 +596,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -627,15 +618,15 @@ describe('group', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'removeUserRandomUser'
|
username: 'removeUserRandomUser'
|
||||||
})
|
})
|
||||||
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.groupId).toBeTruthy()
|
||||||
expect(res.body.name).toEqual(group.name)
|
expect(res.body.name).toEqual(group.name)
|
||||||
expect(res.body.description).toEqual(group.description)
|
expect(res.body.description).toEqual(group.description)
|
||||||
expect(res.body.isActive).toEqual(true)
|
expect(res.body.isActive).toEqual(true)
|
||||||
@@ -648,16 +639,16 @@ describe('group', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'removeGroupFromUser'
|
username: 'removeGroupFromUser'
|
||||||
})
|
})
|
||||||
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${dbUser.uid}`)
|
.get(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -676,7 +667,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -695,7 +686,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -732,10 +723,8 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Not Found if groupId is incorrect', async () => {
|
it('should respond with Not Found if groupId is incorrect', async () => {
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${hexValue}/123`)
|
.delete('/SASjsApi/group/123/123')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -746,10 +735,8 @@ describe('group', () => {
|
|||||||
|
|
||||||
it('should respond with Not Found if userId is incorrect', async () => {
|
it('should respond with Not Found if userId is incorrect', async () => {
|
||||||
const dbGroup = await groupController.createGroup(group)
|
const dbGroup = await groupController.createGroup(group)
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.uid}/${hexValue}`)
|
.delete(`/SASjsApi/group/${dbGroup.groupId}/123`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -765,10 +752,10 @@ const generateSaveTokenAndCreateUser = async (
|
|||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const dbUser = await userController.createUser(someUser ?? adminUser)
|
const dbUser = await userController.createUser(someUser ?? adminUser)
|
||||||
|
|
||||||
return generateAndSaveToken(dbUser.uid)
|
return generateAndSaveToken(dbUser.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: string) => {
|
const generateAndSaveToken = async (userId: number) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
PermissionDetailsResponse
|
PermissionDetailsResponse
|
||||||
} from '../../../controllers'
|
} from '../../../controllers'
|
||||||
import { generateAccessToken, saveTokensInDB } from '../../../utils'
|
import { generateAccessToken, saveTokensInDB } from '../../../utils'
|
||||||
import { randomBytes } from 'crypto'
|
|
||||||
|
|
||||||
const deployPayload = {
|
const deployPayload = {
|
||||||
appLoc: 'string',
|
appLoc: 'string',
|
||||||
@@ -104,10 +103,10 @@ describe('permission', () => {
|
|||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/permission')
|
.post('/SASjsApi/permission')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ ...permission, principalId: dbUser.uid })
|
.send({ ...permission, principalId: dbUser.id })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.permissionId).toBeTruthy()
|
||||||
expect(res.body.path).toEqual(permission.path)
|
expect(res.body.path).toEqual(permission.path)
|
||||||
expect(res.body.type).toEqual(permission.type)
|
expect(res.body.type).toEqual(permission.type)
|
||||||
expect(res.body.setting).toEqual(permission.setting)
|
expect(res.body.setting).toEqual(permission.setting)
|
||||||
@@ -123,11 +122,11 @@ describe('permission', () => {
|
|||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalType: 'group',
|
principalType: 'group',
|
||||||
principalId: dbGroup.uid
|
principalId: dbGroup.groupId
|
||||||
})
|
})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.uid).toBeTruthy()
|
expect(res.body.permissionId).toBeTruthy()
|
||||||
expect(res.body.path).toEqual(permission.path)
|
expect(res.body.path).toEqual(permission.path)
|
||||||
expect(res.body.type).toEqual(permission.type)
|
expect(res.body.type).toEqual(permission.type)
|
||||||
expect(res.body.setting).toEqual(permission.setting)
|
expect(res.body.setting).toEqual(permission.setting)
|
||||||
@@ -145,7 +144,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Unauthorized if access token is not of an admin account', async () => {
|
it('should respond with Unauthorized if access token is not of an admin account', async () => {
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/permission')
|
.post('/SASjsApi/permission')
|
||||||
@@ -282,19 +281,17 @@ describe('permission', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Bad Request if principalId is not a string of 24 hex characters', async () => {
|
it('should respond with Bad Request if principalId is not a number', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/permission')
|
.post('/SASjsApi/permission')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: randomBytes(10).toString('hex')
|
principalId: 'someCharacters'
|
||||||
})
|
})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
expect(res.text).toEqual('"principalId" must be a number')
|
||||||
'"principalId" length must be 24 characters long'
|
|
||||||
)
|
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -310,7 +307,7 @@ describe('permission', () => {
|
|||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: adminUser.uid
|
principalId: adminUser.id
|
||||||
})
|
})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
|
|
||||||
@@ -324,7 +321,7 @@ describe('permission', () => {
|
|||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: randomBytes(12).toString('hex')
|
principalId: 123
|
||||||
})
|
})
|
||||||
.expect(404)
|
.expect(404)
|
||||||
|
|
||||||
@@ -339,7 +336,7 @@ describe('permission', () => {
|
|||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalType: 'group',
|
principalType: 'group',
|
||||||
principalId: randomBytes(12).toString('hex')
|
principalId: 123
|
||||||
})
|
})
|
||||||
.expect(404)
|
.expect(404)
|
||||||
|
|
||||||
@@ -350,13 +347,13 @@ describe('permission', () => {
|
|||||||
it('should respond with Conflict (409) if permission already exists', async () => {
|
it('should respond with Conflict (409) if permission already exists', async () => {
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/permission')
|
.post('/SASjsApi/permission')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ ...permission, principalId: dbUser.uid })
|
.send({ ...permission, principalId: dbUser.id })
|
||||||
.expect(409)
|
.expect(409)
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
expect(res.text).toEqual(
|
||||||
@@ -371,7 +368,7 @@ describe('permission', () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
dbPermission = await permissionController.createPermission({
|
dbPermission = await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -381,7 +378,7 @@ describe('permission', () => {
|
|||||||
|
|
||||||
it('should respond with updated permission', async () => {
|
it('should respond with updated permission', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ setting: PermissionSettingForRoute.deny })
|
.send({ setting: PermissionSettingForRoute.deny })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -391,7 +388,7 @@ describe('permission', () => {
|
|||||||
|
|
||||||
it('should respond with Unauthorized if access token is not present', async () => {
|
it('should respond with Unauthorized if access token is not present', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
||||||
.send()
|
.send()
|
||||||
.expect(401)
|
.expect(401)
|
||||||
|
|
||||||
@@ -406,7 +403,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -417,7 +414,7 @@ describe('permission', () => {
|
|||||||
|
|
||||||
it('should respond with Bad Request if setting is missing', async () => {
|
it('should respond with Bad Request if setting is missing', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -428,7 +425,7 @@ describe('permission', () => {
|
|||||||
|
|
||||||
it('should respond with Bad Request if setting is invalid', async () => {
|
it('should respond with Bad Request if setting is invalid', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
setting: 'invalid'
|
setting: 'invalid'
|
||||||
@@ -440,9 +437,8 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with not found (404) if permission with provided id does not exist', async () => {
|
it('should respond with not found (404) if permission with provided id does not exist', async () => {
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${hexValue}`)
|
.patch('/SASjsApi/permission/123')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
setting: PermissionSettingForRoute.deny
|
setting: PermissionSettingForRoute.deny
|
||||||
@@ -458,10 +454,10 @@ describe('permission', () => {
|
|||||||
it('should delete permission', async () => {
|
it('should delete permission', async () => {
|
||||||
const dbPermission = await permissionController.createPermission({
|
const dbPermission = await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/permission/${dbPermission?.uid}`)
|
.delete(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -470,10 +466,8 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with not found (404) if permission with provided id does not exists', async () => {
|
it('should respond with not found (404) if permission with provided id does not exists', async () => {
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/permission/${hexValue}`)
|
.delete('/SASjsApi/permission/123')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -487,12 +481,12 @@ describe('permission', () => {
|
|||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/test-1',
|
path: '/test-1',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/test-2',
|
path: '/test-2',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -511,12 +505,12 @@ describe('permission', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'get' + user.username
|
username: 'get' + user.username
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(nonAdminUser.uid)
|
const accessToken = await generateAndSaveToken(nonAdminUser.id)
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
path: '/test-1',
|
path: '/test-1',
|
||||||
type: PermissionType.route,
|
type: PermissionType.route,
|
||||||
principalType: PrincipalType.user,
|
principalType: PrincipalType.user,
|
||||||
principalId: nonAdminUser.uid,
|
principalId: nonAdminUser.id,
|
||||||
setting: PermissionSettingForRoute.grant
|
setting: PermissionSettingForRoute.grant
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -537,7 +531,7 @@ describe('permission', () => {
|
|||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/deploy',
|
path: '/SASjsApi/drive/deploy',
|
||||||
principalId: dbUser.uid
|
principalId: dbUser.id
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -557,7 +551,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create files in SASJS drive', async () => {
|
it('should create files in SASJS drive', async () => {
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get('/SASjsApi/drive/deploy')
|
.get('/SASjsApi/drive/deploy')
|
||||||
@@ -567,7 +561,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond unauthorized', async () => {
|
it('should respond unauthorized', async () => {
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get('/SASjsApi/drive/deploy/upload')
|
.get('/SASjsApi/drive/deploy/upload')
|
||||||
@@ -583,10 +577,10 @@ const generateSaveTokenAndCreateUser = async (
|
|||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const dbUser = await userController.createUser(someUser ?? adminUser)
|
const dbUser = await userController.createUser(someUser ?? adminUser)
|
||||||
|
|
||||||
return generateAndSaveToken(dbUser.uid)
|
return generateAndSaveToken(dbUser.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: string) => {
|
const generateAndSaveToken = async (userId: number) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
SASSessionController
|
SASSessionController
|
||||||
} from '../../../controllers/internal'
|
} from '../../../controllers/internal'
|
||||||
import * as ProcessProgramModule from '../../../controllers/internal/processProgram'
|
import * as ProcessProgramModule from '../../../controllers/internal/processProgram'
|
||||||
import { Session, SessionState } from '../../../types'
|
import { Session } from '../../../types'
|
||||||
|
|
||||||
const clientId = 'someclientID'
|
const clientId = 'someclientID'
|
||||||
|
|
||||||
@@ -58,12 +58,12 @@ describe('stp', () => {
|
|||||||
mongoServer = await MongoMemoryServer.create()
|
mongoServer = await MongoMemoryServer.create()
|
||||||
con = await mongoose.connect(mongoServer.getUri())
|
con = await mongoose.connect(mongoServer.getUri())
|
||||||
const dbUser = await userController.createUser(user)
|
const dbUser = await userController.createUser(user)
|
||||||
accessToken = await generateAndSaveToken(dbUser.uid)
|
accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
path: '/SASjsApi/stp/execute',
|
path: '/SASjsApi/stp/execute',
|
||||||
type: PermissionType.route,
|
type: PermissionType.route,
|
||||||
principalType: PrincipalType.user,
|
principalType: PrincipalType.user,
|
||||||
principalId: dbUser.uid,
|
principalId: dbUser.id,
|
||||||
setting: PermissionSettingForRoute.grant
|
setting: PermissionSettingForRoute.grant
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -456,7 +456,7 @@ const makeRequestAndAssert = async (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: string) => {
|
const generateAndSaveToken = async (userId: number) => {
|
||||||
const accessToken = generateAccessToken({
|
const accessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
@@ -493,7 +493,10 @@ const mockedGetSession = async () => {
|
|||||||
|
|
||||||
const session: Session = {
|
const session: Session = {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
state: SessionState.pending,
|
ready: true,
|
||||||
|
inUse: true,
|
||||||
|
consumed: false,
|
||||||
|
completed: false,
|
||||||
creationTimeStamp,
|
creationTimeStamp,
|
||||||
deathTimeStamp,
|
deathTimeStamp,
|
||||||
path: sessionFolder
|
path: sessionFolder
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { randomBytes } from 'crypto'
|
|
||||||
import { Express } from 'express'
|
import { Express } from 'express'
|
||||||
import mongoose, { Mongoose } from 'mongoose'
|
import mongoose, { Mongoose } from 'mongoose'
|
||||||
import { MongoMemoryServer } from 'mongodb-memory-server'
|
import { MongoMemoryServer } from 'mongodb-memory-server'
|
||||||
@@ -102,9 +101,9 @@ describe('user', () => {
|
|||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = generateAccessToken({
|
const accessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId: dbUser.uid
|
userId: dbUser.id
|
||||||
})
|
})
|
||||||
await saveTokensInDB(dbUser.uid, clientId, accessToken, 'refreshToken')
|
await saveTokensInDB(dbUser.id, clientId, accessToken, 'refreshToken')
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/user')
|
.post('/SASjsApi/user')
|
||||||
@@ -188,7 +187,7 @@ describe('user', () => {
|
|||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser.uid}`)
|
.patch(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ ...user, displayName: newDisplayName })
|
.send({ ...user, displayName: newDisplayName })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -201,11 +200,11 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with updated user when user himself requests', async () => {
|
it('should respond with updated user when user himself requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser.uid}`)
|
.patch(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
displayName: newDisplayName,
|
displayName: newDisplayName,
|
||||||
@@ -222,11 +221,11 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with Bad Request, only admin can update isAdmin/isActive', async () => {
|
it('should respond with Bad Request, only admin can update isAdmin/isActive', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser.uid}`)
|
.patch(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ ...user, displayName: newDisplayName })
|
.send({ ...user, displayName: newDisplayName })
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -278,10 +277,10 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
const accessToken = await generateAndSaveToken(dbUser2.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser1.uid}`)
|
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -298,7 +297,7 @@ describe('user', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser1.uid}`)
|
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ username: dbUser2.username })
|
.send({ username: dbUser2.username })
|
||||||
.expect(409)
|
.expect(409)
|
||||||
@@ -326,7 +325,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with updated user when user himself requests', async () => {
|
it('should respond with updated user when user himself requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -347,7 +346,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with Bad Request, only admin can update isAdmin/isActive', async () => {
|
it('should respond with Bad Request, only admin can update isAdmin/isActive', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
@@ -373,10 +372,10 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
const accessToken = await generateAndSaveToken(dbUser2.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser1.uid}`)
|
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -419,7 +418,7 @@ describe('user', () => {
|
|||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
.delete(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -429,10 +428,10 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with OK when user himself requests', async () => {
|
it('should respond with OK when user himself requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
.delete(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ password: user.password })
|
.send({ password: user.password })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -442,10 +441,10 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with Bad Request when user himself requests and password is missing', async () => {
|
it('should respond with Bad Request when user himself requests and password is missing', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
.delete(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -470,10 +469,10 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
const accessToken = await generateAndSaveToken(dbUser2.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser1.uid}`)
|
.delete(`/SASjsApi/user/${dbUser1.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -484,10 +483,10 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with Unauthorized when user himself requests and password is incorrect', async () => {
|
it('should respond with Unauthorized when user himself requests and password is incorrect', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
.delete(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ password: 'incorrectpassword' })
|
.send({ password: 'incorrectpassword' })
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -511,7 +510,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with OK when user himself requests', async () => {
|
it('should respond with OK when user himself requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
@@ -524,7 +523,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with Bad Request when user himself requests and password is missing', async () => {
|
it('should respond with Bad Request when user himself requests and password is missing', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
@@ -552,7 +551,7 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
const accessToken = await generateAndSaveToken(dbUser2.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser1.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser1.username}`)
|
||||||
@@ -566,7 +565,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with Unauthorized when user himself requests and password is incorrect', async () => {
|
it('should respond with Unauthorized when user himself requests and password is incorrect', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.uid)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
@@ -593,7 +592,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with user autoExec when same user requests', async () => {
|
it('should respond with user autoExec when same user requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const userId = dbUser.uid
|
const userId = dbUser.id
|
||||||
const accessToken = await generateAndSaveToken(userId)
|
const accessToken = await generateAndSaveToken(userId)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -612,7 +611,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with user autoExec when admin user requests', async () => {
|
it('should respond with user autoExec when admin user requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const userId = dbUser.uid
|
const userId = dbUser.id
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${userId}`)
|
.get(`/SASjsApi/user/${userId}`)
|
||||||
@@ -635,7 +634,7 @@ describe('user', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const userId = dbUser.uid
|
const userId = dbUser.id
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${userId}`)
|
.get(`/SASjsApi/user/${userId}`)
|
||||||
@@ -653,7 +652,7 @@ describe('user', () => {
|
|||||||
|
|
||||||
it('should respond with user along with associated groups', async () => {
|
it('should respond with user along with associated groups', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const userId = dbUser.uid
|
const userId = dbUser.id
|
||||||
const accessToken = await generateAndSaveToken(userId)
|
const accessToken = await generateAndSaveToken(userId)
|
||||||
|
|
||||||
const group = {
|
const group = {
|
||||||
@@ -662,7 +661,7 @@ describe('user', () => {
|
|||||||
}
|
}
|
||||||
const groupController = new GroupController()
|
const groupController = new GroupController()
|
||||||
const dbGroup = await groupController.createGroup(group)
|
const dbGroup = await groupController.createGroup(group)
|
||||||
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${userId}`)
|
.get(`/SASjsApi/user/${userId}`)
|
||||||
@@ -691,10 +690,8 @@ describe('user', () => {
|
|||||||
it('should respond with Not Found if userId is incorrect', async () => {
|
it('should respond with Not Found if userId is incorrect', async () => {
|
||||||
await controller.createUser(user)
|
await controller.createUser(user)
|
||||||
|
|
||||||
const hexValue = randomBytes(12).toString('hex')
|
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${hexValue}`)
|
.get('/SASjsApi/user/1234')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -706,7 +703,7 @@ describe('user', () => {
|
|||||||
describe('by username', () => {
|
describe('by username', () => {
|
||||||
it('should respond with user autoExec when same user requests', async () => {
|
it('should respond with user autoExec when same user requests', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const userId = dbUser.uid
|
const userId = dbUser.id
|
||||||
const accessToken = await generateAndSaveToken(userId)
|
const accessToken = await generateAndSaveToken(userId)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -806,13 +803,13 @@ describe('user', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
id: expect.anything(),
|
||||||
username: adminUser.username,
|
username: adminUser.username,
|
||||||
displayName: adminUser.displayName,
|
displayName: adminUser.displayName,
|
||||||
isAdmin: adminUser.isAdmin
|
isAdmin: adminUser.isAdmin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
id: expect.anything(),
|
||||||
username: user.username,
|
username: user.username,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
isAdmin: user.isAdmin
|
isAdmin: user.isAdmin
|
||||||
@@ -834,13 +831,13 @@ describe('user', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
id: expect.anything(),
|
||||||
username: adminUser.username,
|
username: adminUser.username,
|
||||||
displayName: adminUser.displayName,
|
displayName: adminUser.displayName,
|
||||||
isAdmin: adminUser.isAdmin
|
isAdmin: adminUser.isAdmin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uid: expect.anything(),
|
id: expect.anything(),
|
||||||
username: 'randomUser',
|
username: 'randomUser',
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
isAdmin: user.isAdmin
|
isAdmin: user.isAdmin
|
||||||
@@ -862,10 +859,10 @@ const generateSaveTokenAndCreateUser = async (
|
|||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const dbUser = await controller.createUser(someUser ?? adminUser)
|
const dbUser = await controller.createUser(someUser ?? adminUser)
|
||||||
|
|
||||||
return generateAndSaveToken(dbUser.uid)
|
return generateAndSaveToken(dbUser.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: string) => {
|
const generateAndSaveToken = async (userId: number) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ describe('web', () => {
|
|||||||
|
|
||||||
expect(res.body.loggedIn).toBeTruthy()
|
expect(res.body.loggedIn).toBeTruthy()
|
||||||
expect(res.body.user).toEqual({
|
expect(res.body.user).toEqual({
|
||||||
id: expect.any(String),
|
id: expect.any(Number),
|
||||||
username: user.username,
|
username: user.username,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
isAdmin: user.isAdmin,
|
isAdmin: user.isAdmin,
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import {
|
import { executeProgramRawValidation } from '../../utils'
|
||||||
executeProgramRawValidation,
|
|
||||||
triggerProgramValidation
|
|
||||||
} from '../../utils'
|
|
||||||
import { STPController } from '../../controllers/'
|
import { STPController } from '../../controllers/'
|
||||||
import { FileUploadController } from '../../controllers/internal'
|
import { FileUploadController } from '../../controllers/internal'
|
||||||
|
|
||||||
@@ -72,28 +69,4 @@ stpRouter.post(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
stpRouter.post('/trigger', async (req, res) => {
|
|
||||||
const { error, value: query } = triggerProgramValidation(req.query)
|
|
||||||
|
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await controller.triggerProgram(
|
|
||||||
req,
|
|
||||||
query._program,
|
|
||||||
query._debug,
|
|
||||||
query.expiresAfterMins
|
|
||||||
)
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
const statusCode = err.code
|
|
||||||
|
|
||||||
delete err.code
|
|
||||||
|
|
||||||
res.status(statusCode).send(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default stpRouter
|
export default stpRouter
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
deleteUserValidation,
|
deleteUserValidation,
|
||||||
getUserValidation,
|
getUserValidation,
|
||||||
registerUserValidation,
|
registerUserValidation,
|
||||||
uidValidation,
|
|
||||||
updateUserValidation
|
updateUserValidation
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
@@ -57,15 +56,12 @@ userRouter.get(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
userRouter.get('/:uid', authenticateAccessToken, async (req, res) => {
|
userRouter.get('/:userId', authenticateAccessToken, async (req, res) => {
|
||||||
const { error, value: params } = uidValidation(req.params)
|
const { userId } = req.params
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
|
||||||
|
|
||||||
const { uid } = params
|
|
||||||
|
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.getUser(req, uid)
|
const response = await controller.getUser(req, parseInt(userId))
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
@@ -101,16 +97,12 @@ userRouter.patch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
userRouter.patch(
|
userRouter.patch(
|
||||||
'/:uid',
|
'/:userId',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdminIfNeeded,
|
verifyAdminIfNeeded,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { user } = req
|
const { user } = req
|
||||||
|
const { userId } = req.params
|
||||||
const { error: uidError, value: params } = uidValidation(req.params)
|
|
||||||
if (uidError) return res.status(400).send(uidError.details[0].message)
|
|
||||||
|
|
||||||
const { uid } = params
|
|
||||||
|
|
||||||
// only an admin can update `isActive` and `isAdmin` fields
|
// only an admin can update `isActive` and `isAdmin` fields
|
||||||
const { error, value: body } = updateUserValidation(req.body, user!.isAdmin)
|
const { error, value: body } = updateUserValidation(req.body, user!.isAdmin)
|
||||||
@@ -118,7 +110,7 @@ userRouter.patch(
|
|||||||
|
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.updateUser(uid, body)
|
const response = await controller.updateUser(parseInt(userId), body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
@@ -155,16 +147,12 @@ userRouter.delete(
|
|||||||
)
|
)
|
||||||
|
|
||||||
userRouter.delete(
|
userRouter.delete(
|
||||||
'/:uid',
|
'/:userId',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdminIfNeeded,
|
verifyAdminIfNeeded,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { user } = req
|
const { user } = req
|
||||||
|
const { userId } = req.params
|
||||||
const { error: uidError, value: params } = uidValidation(req.params)
|
|
||||||
if (uidError) return res.status(400).send(uidError.details[0].message)
|
|
||||||
|
|
||||||
const { uid } = params
|
|
||||||
|
|
||||||
// only an admin can delete user without providing password
|
// only an admin can delete user without providing password
|
||||||
const { error, value: data } = deleteUserValidation(req.body, user!.isAdmin)
|
const { error, value: data } = deleteUserValidation(req.body, user!.isAdmin)
|
||||||
@@ -172,7 +160,7 @@ userRouter.delete(
|
|||||||
|
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
try {
|
try {
|
||||||
await controller.deleteUser(uid, data, user!.isAdmin)
|
await controller.deleteUser(parseInt(userId), data, user!.isAdmin)
|
||||||
res.status(200).send('Account Deleted!')
|
res.status(200).send('Account Deleted!')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(err.code).send(err.message)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface InfoJWT {
|
export interface InfoJWT {
|
||||||
clientId: string
|
clientId: string
|
||||||
userId: string
|
userId: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface PreProgramVars {
|
export interface PreProgramVars {
|
||||||
username: string
|
username: string
|
||||||
userId: string
|
userId: number
|
||||||
displayName: string
|
displayName: string
|
||||||
serverUrl: string
|
serverUrl: string
|
||||||
httpHeaders: string[]
|
httpHeaders: string[]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface RequestUser {
|
export interface RequestUser {
|
||||||
userId: string
|
userId: number
|
||||||
clientId: string
|
clientId: string
|
||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
export enum SessionState {
|
|
||||||
initialising = 'initialising', // session is initialising and not ready to be used yet
|
|
||||||
pending = 'pending', // session is ready to be used
|
|
||||||
running = 'running', // session is in use
|
|
||||||
completed = 'completed', // session is completed and can be destroyed
|
|
||||||
failed = 'failed' // session failed
|
|
||||||
}
|
|
||||||
export interface Session {
|
export interface Session {
|
||||||
id: string
|
id: string
|
||||||
state: SessionState
|
ready: boolean
|
||||||
creationTimeStamp: string
|
creationTimeStamp: string
|
||||||
deathTimeStamp: string
|
deathTimeStamp: string
|
||||||
path: string
|
path: string
|
||||||
expiresAfterMins?: { mins: number; used: boolean }
|
inUse: boolean
|
||||||
failureReason?: string
|
consumed: boolean
|
||||||
|
completed: boolean
|
||||||
|
crashed?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
import { randomBytes } from 'crypto'
|
|
||||||
|
|
||||||
export const randomBytesHexString = (bytesCount: number) =>
|
|
||||||
randomBytes(bytesCount).toString('hex')
|
|
||||||
@@ -22,7 +22,7 @@ export const getPreProgramVariables = (req: Request): PreProgramVars => {
|
|||||||
//So this is workaround.
|
//So this is workaround.
|
||||||
return {
|
return {
|
||||||
username: user ? user.username : 'demo',
|
username: user ? user.username : 'demo',
|
||||||
userId: user ? user.userId : 'demoId',
|
userId: user ? user.userId : 0,
|
||||||
displayName: user ? user.displayName : 'demo',
|
displayName: user ? user.displayName : 'demo',
|
||||||
serverUrl: protocol + host,
|
serverUrl: protocol + host,
|
||||||
httpHeaders
|
httpHeaders
|
||||||
|
|||||||
15
api/src/utils/getSequenceNextValue.ts
Normal file
15
api/src/utils/getSequenceNextValue.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Counter from '../model/Counter'
|
||||||
|
|
||||||
|
export const getSequenceNextValue = async (seqName: string) => {
|
||||||
|
const seqDoc = await Counter.findOne({ id: seqName })
|
||||||
|
if (!seqDoc) {
|
||||||
|
await Counter.create({ id: seqName, seq: 1 })
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
seqDoc.seq += 1
|
||||||
|
|
||||||
|
await seqDoc.save()
|
||||||
|
|
||||||
|
return seqDoc.seq
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import User from '../model/User'
|
|||||||
const isValidToken = async (
|
const isValidToken = async (
|
||||||
token: string,
|
token: string,
|
||||||
key: string,
|
key: string,
|
||||||
userId: string,
|
userId: number,
|
||||||
clientId: string
|
clientId: string
|
||||||
) => {
|
) => {
|
||||||
const promise = new Promise<boolean>((resolve, reject) =>
|
const promise = new Promise<boolean>((resolve, reject) =>
|
||||||
@@ -22,8 +22,8 @@ const isValidToken = async (
|
|||||||
return await promise.then(() => true).catch(() => false)
|
return await promise.then(() => true).catch(() => false)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTokensFromDB = async (userId: string, clientId: string) => {
|
export const getTokensFromDB = async (userId: number, clientId: string) => {
|
||||||
const user = await User.findOne({ _id: userId })
|
const user = await User.findOne({ id: userId })
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
const currentTokenObj = user.tokens.find(
|
const currentTokenObj = user.tokens.find(
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ export * from './appStreamConfig'
|
|||||||
export * from './connectDB'
|
export * from './connectDB'
|
||||||
export * from './copySASjsCore'
|
export * from './copySASjsCore'
|
||||||
export * from './createWeboutSasFile'
|
export * from './createWeboutSasFile'
|
||||||
export * from './crypto'
|
|
||||||
export * from './desktopAutoExec'
|
export * from './desktopAutoExec'
|
||||||
export * from './extractHeaders'
|
export * from './extractHeaders'
|
||||||
export * from './extractName'
|
export * from './extractName'
|
||||||
@@ -15,6 +14,7 @@ export * from './getCertificates'
|
|||||||
export * from './getDesktopFields'
|
export * from './getDesktopFields'
|
||||||
export * from './getPreProgramVariables'
|
export * from './getPreProgramVariables'
|
||||||
export * from './getRunTimeAndFilePath'
|
export * from './getRunTimeAndFilePath'
|
||||||
|
export * from './getSequenceNextValue'
|
||||||
export * from './getServerUrl'
|
export * from './getServerUrl'
|
||||||
export * from './getTokensFromDB'
|
export * from './getTokensFromDB'
|
||||||
export * from './instantiateLogger'
|
export * from './instantiateLogger'
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const isPublicRoute = async (req: Request): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const publicUser: RequestUser = {
|
export const publicUser: RequestUser = {
|
||||||
userId: 'public_user_id',
|
userId: 0,
|
||||||
clientId: 'public_app',
|
clientId: 'public_app',
|
||||||
username: 'publicUser',
|
username: 'publicUser',
|
||||||
displayName: 'Public User',
|
displayName: 'Public User',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import User from '../model/User'
|
import User from '../model/User'
|
||||||
|
|
||||||
export const removeTokensInDB = async (userId: string, clientId: string) => {
|
export const removeTokensInDB = async (userId: number, clientId: string) => {
|
||||||
const user = await User.findOne({ _id: userId })
|
const user = await User.findOne({ id: userId })
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
const tokenObjIndex = user.tokens.findIndex(
|
const tokenObjIndex = user.tokens.findIndex(
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import User from '../model/User'
|
import User from '../model/User'
|
||||||
|
|
||||||
export const saveTokensInDB = async (
|
export const saveTokensInDB = async (
|
||||||
userId: string,
|
userId: number,
|
||||||
clientId: string,
|
clientId: string,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
refreshToken: string
|
refreshToken: string
|
||||||
) => {
|
) => {
|
||||||
const user = await User.findOne({ _id: userId })
|
const user = await User.findOne({ id: userId })
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
const currentTokenObj = user.tokens.find(
|
const currentTokenObj = user.tokens.find(
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export const seedDB = async (): Promise<ConfigurationType> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ALL_USERS_GROUP = {
|
export const ALL_USERS_GROUP = {
|
||||||
name: 'all-users',
|
name: 'AllUsers',
|
||||||
description: 'Group contains all users'
|
description: 'Group contains all users'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,9 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import {
|
import { createFolder, getAbsolutePath, getRealPath } from '@sasjs/utils'
|
||||||
createFolder,
|
|
||||||
getAbsolutePath,
|
|
||||||
getRealPath,
|
|
||||||
fileExists
|
|
||||||
} from '@sasjs/utils'
|
|
||||||
import dotenv from 'dotenv'
|
|
||||||
import { connectDB, getDesktopFields, ModeType, RunTimeType, SECRETS } from '.'
|
import { connectDB, getDesktopFields, ModeType, RunTimeType, SECRETS } from '.'
|
||||||
|
|
||||||
export const setProcessVariables = async () => {
|
export const setProcessVariables = async () => {
|
||||||
const { execPath } = process
|
|
||||||
|
|
||||||
// Check if execPath ends with 'api-macos' to determine executable for MacOS.
|
|
||||||
// This is needed to fix picking .env file issue in MacOS executable.
|
|
||||||
if (execPath) {
|
|
||||||
const envPathSplitted = execPath.split(path.sep)
|
|
||||||
|
|
||||||
if (envPathSplitted.pop() === 'api-macos') {
|
|
||||||
const envPath = path.join(envPathSplitted.join(path.sep), '.env')
|
|
||||||
|
|
||||||
// Override environment variables from envPath if file exists
|
|
||||||
if (await fileExists(envPath)) {
|
|
||||||
dotenv.config({ path: envPath, override: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { MODE, RUN_TIMES } = process.env
|
const { MODE, RUN_TIMES } = process.env
|
||||||
|
|
||||||
if (MODE === ModeType.Server) {
|
if (MODE === ModeType.Server) {
|
||||||
@@ -43,7 +21,6 @@ export const setProcessVariables = async () => {
|
|||||||
if (process.env.NODE_ENV === 'test') {
|
if (process.env.NODE_ENV === 'test') {
|
||||||
process.sasjsRoot = path.join(process.cwd(), 'sasjs_root')
|
process.sasjsRoot = path.join(process.cwd(), 'sasjs_root')
|
||||||
process.driveLoc = path.join(process.cwd(), 'sasjs_root', 'drive')
|
process.driveLoc = path.join(process.cwd(), 'sasjs_root', 'drive')
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,9 +41,7 @@ export const setProcessVariables = async () => {
|
|||||||
|
|
||||||
const { SASJS_ROOT } = process.env
|
const { SASJS_ROOT } = process.env
|
||||||
const absPath = getAbsolutePath(SASJS_ROOT ?? 'sasjs_root', process.cwd())
|
const absPath = getAbsolutePath(SASJS_ROOT ?? 'sasjs_root', process.cwd())
|
||||||
|
|
||||||
await createFolder(absPath)
|
await createFolder(absPath)
|
||||||
|
|
||||||
process.sasjsRoot = getRealPath(absPath)
|
process.sasjsRoot = getRealPath(absPath)
|
||||||
|
|
||||||
const { DRIVE_LOCATION } = process.env
|
const { DRIVE_LOCATION } = process.env
|
||||||
@@ -74,7 +49,6 @@ export const setProcessVariables = async () => {
|
|||||||
DRIVE_LOCATION ?? path.join(process.sasjsRoot, 'drive'),
|
DRIVE_LOCATION ?? path.join(process.sasjsRoot, 'drive'),
|
||||||
process.cwd()
|
process.cwd()
|
||||||
)
|
)
|
||||||
|
|
||||||
await createFolder(absDrivePath)
|
await createFolder(absDrivePath)
|
||||||
process.driveLoc = getRealPath(absDrivePath)
|
process.driveLoc = getRealPath(absDrivePath)
|
||||||
|
|
||||||
@@ -83,9 +57,7 @@ export const setProcessVariables = async () => {
|
|||||||
LOG_LOCATION ?? path.join(process.sasjsRoot, 'logs'),
|
LOG_LOCATION ?? path.join(process.sasjsRoot, 'logs'),
|
||||||
process.cwd()
|
process.cwd()
|
||||||
)
|
)
|
||||||
|
|
||||||
await createFolder(absLogsPath)
|
await createFolder(absLogsPath)
|
||||||
|
|
||||||
process.logsLoc = getRealPath(absLogsPath)
|
process.logsLoc = getRealPath(absLogsPath)
|
||||||
|
|
||||||
process.logsUUID = 'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
|
process.logsUUID = 'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ const groupnameSchema = Joi.string().lowercase().alphanum().min(3).max(16)
|
|||||||
|
|
||||||
export const blockFileRegex = /\.(exe|sh|htaccess)$/i
|
export const blockFileRegex = /\.(exe|sh|htaccess)$/i
|
||||||
|
|
||||||
export const uidValidation = (data: any) =>
|
|
||||||
Joi.object({
|
|
||||||
uid: Joi.string().length(24).hex().required()
|
|
||||||
}).validate(data)
|
|
||||||
|
|
||||||
export const getUserValidation = (data: any): Joi.ValidationResult =>
|
export const getUserValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
Joi.object({
|
||||||
username: usernameSchema.required()
|
username: usernameSchema.required()
|
||||||
@@ -118,7 +113,7 @@ export const registerPermissionValidation = (data: any): Joi.ValidationResult =>
|
|||||||
principalType: Joi.string()
|
principalType: Joi.string()
|
||||||
.required()
|
.required()
|
||||||
.valid(...Object.values(PrincipalType)),
|
.valid(...Object.values(PrincipalType)),
|
||||||
principalId: Joi.string().length(24).hex().required()
|
principalId: Joi.number().required()
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
export const updatePermissionValidation = (data: any): Joi.ValidationResult =>
|
export const updatePermissionValidation = (data: any): Joi.ValidationResult =>
|
||||||
@@ -183,13 +178,6 @@ export const runCodeValidation = (data: any): Joi.ValidationResult =>
|
|||||||
runTime: Joi.string().valid(...process.runTimes)
|
runTime: Joi.string().valid(...process.runTimes)
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
export const triggerCodeValidation = (data: any): Joi.ValidationResult =>
|
|
||||||
Joi.object({
|
|
||||||
code: Joi.string().required(),
|
|
||||||
runTime: Joi.string().valid(...process.runTimes),
|
|
||||||
expiresAfterMins: Joi.number().greater(0)
|
|
||||||
}).validate(data)
|
|
||||||
|
|
||||||
export const executeProgramRawValidation = (data: any): Joi.ValidationResult =>
|
export const executeProgramRawValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
Joi.object({
|
||||||
_program: Joi.string().required(),
|
_program: Joi.string().required(),
|
||||||
@@ -197,17 +185,3 @@ export const executeProgramRawValidation = (data: any): Joi.ValidationResult =>
|
|||||||
})
|
})
|
||||||
.pattern(/^/, Joi.alternatives(Joi.string(), Joi.number()))
|
.pattern(/^/, Joi.alternatives(Joi.string(), Joi.number()))
|
||||||
.validate(data)
|
.validate(data)
|
||||||
|
|
||||||
export const triggerProgramValidation = (data: any): Joi.ValidationResult =>
|
|
||||||
Joi.object({
|
|
||||||
_program: Joi.string().required(),
|
|
||||||
_debug: Joi.number(),
|
|
||||||
expiresAfterMins: Joi.number().greater(0)
|
|
||||||
})
|
|
||||||
.pattern(/^/, Joi.alternatives(Joi.string(), Joi.number()))
|
|
||||||
.validate(data)
|
|
||||||
|
|
||||||
export const sessionIdValidation = (data: any): Joi.ValidationResult =>
|
|
||||||
Joi.object({
|
|
||||||
sessionId: Joi.string().required()
|
|
||||||
}).validate(data)
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { RequestUser } from '../types'
|
|||||||
export const fetchLatestAutoExec = async (
|
export const fetchLatestAutoExec = async (
|
||||||
reqUser: RequestUser
|
reqUser: RequestUser
|
||||||
): Promise<RequestUser | undefined> => {
|
): Promise<RequestUser | undefined> => {
|
||||||
const dbUser = await User.findOne({ _id: reqUser.userId })
|
const dbUser = await User.findOne({ id: reqUser.userId })
|
||||||
|
|
||||||
if (!dbUser) return undefined
|
if (!dbUser) return undefined
|
||||||
|
|
||||||
@@ -21,12 +21,12 @@ export const fetchLatestAutoExec = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const verifyTokenInDB = async (
|
export const verifyTokenInDB = async (
|
||||||
userId: string,
|
userId: number,
|
||||||
clientId: string,
|
clientId: string,
|
||||||
token: string,
|
token: string,
|
||||||
tokenType: 'accessToken' | 'refreshToken'
|
tokenType: 'accessToken' | 'refreshToken'
|
||||||
): Promise<RequestUser | undefined> => {
|
): Promise<RequestUser | undefined> => {
|
||||||
const dbUser = await User.findOne({ _id: userId })
|
const dbUser = await User.findOne({ id: userId })
|
||||||
|
|
||||||
if (!dbUser) return undefined
|
if (!dbUser) return undefined
|
||||||
|
|
||||||
|
|||||||
21
web/package-lock.json
generated
21
web/package-lock.json
generated
@@ -56,7 +56,7 @@
|
|||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-webpack-plugin": "5.5.0",
|
"html-webpack-plugin": "5.5.0",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^3.0.3",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.44.0",
|
||||||
"sass-loader": "^12.3.0",
|
"sass-loader": "^12.3.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
@@ -9413,15 +9413,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.4.1",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
|
||||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.0"
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pretty-error": {
|
"node_modules/pretty-error": {
|
||||||
@@ -18615,9 +18618,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "2.4.1",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
|
||||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pretty-error": {
|
"pretty-error": {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --config webpack.dev.ts --hot",
|
"start": "webpack-dev-server --config webpack.dev.ts --hot",
|
||||||
"build": "webpack --config webpack.prod.ts"
|
"build": "webpack --config webpack.prod.ts",
|
||||||
|
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||||
|
"lint:fix": "npx prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-webpack-plugin": "5.5.0",
|
"html-webpack-plugin": "5.5.0",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^3.0.3",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.44.0",
|
||||||
"sass-loader": "^12.3.0",
|
"sass-loader": "^12.3.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ const AddPermissionModal = ({
|
|||||||
principalType: principalType.toLowerCase(),
|
principalType: principalType.toLowerCase(),
|
||||||
principalId:
|
principalId:
|
||||||
principalType.toLowerCase() === 'user'
|
principalType.toLowerCase() === 'user'
|
||||||
? userPrincipal?.uid
|
? userPrincipal?.id
|
||||||
: groupPrincipal?.uid
|
: groupPrincipal?.groupId
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions.push(addPermissionPayload)
|
permissions.push(addPermissionPayload)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const PermissionTable = ({
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{permissions.map((permission) => (
|
{permissions.map((permission) => (
|
||||||
<TableRow key={permission.uid}>
|
<TableRow key={permission.permissionId}>
|
||||||
<BootstrapTableCell>{permission.path}</BootstrapTableCell>
|
<BootstrapTableCell>{permission.path}</BootstrapTableCell>
|
||||||
<BootstrapTableCell>{permission.type}</BootstrapTableCell>
|
<BootstrapTableCell>{permission.type}</BootstrapTableCell>
|
||||||
<BootstrapTableCell>
|
<BootstrapTableCell>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const useAddPermission = () => {
|
|||||||
|
|
||||||
for (const permission of updatingPermissions) {
|
for (const permission of updatingPermissions) {
|
||||||
await axios
|
await axios
|
||||||
.patch(`/SASjsApi/permission/${permission.uid}`, {
|
.patch(`/SASjsApi/permission/${permission.permissionId}`, {
|
||||||
setting: permission.setting === 'Grant' ? 'Deny' : 'Grant'
|
setting: permission.setting === 'Grant' ? 'Deny' : 'Grant'
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const useDeletePermissionModal = () => {
|
|||||||
setDeleteConfirmationModalOpen(false)
|
setDeleteConfirmationModalOpen(false)
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
axios
|
axios
|
||||||
.delete(`/SASjsApi/permission/${selectedPermission?.uid}`)
|
.delete(`/SASjsApi/permission/${selectedPermission?.permissionId}`)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
fetchPermissions()
|
fetchPermissions()
|
||||||
setSnackbarMessage('Permission deleted!')
|
setSnackbarMessage('Permission deleted!')
|
||||||
|
|||||||
@@ -62,17 +62,21 @@ const useFilterPermissions = () => {
|
|||||||
: permissions
|
: permissions
|
||||||
|
|
||||||
let filteredArray = uriFilteredPermissions.filter((permission) =>
|
let filteredArray = uriFilteredPermissions.filter((permission) =>
|
||||||
principalFilteredPermissions.some((item) => item.uid === permission.uid)
|
principalFilteredPermissions.some(
|
||||||
)
|
(item) => item.permissionId === permission.permissionId
|
||||||
|
|
||||||
filteredArray = filteredArray.filter((permission) =>
|
|
||||||
principalTypeFilteredPermissions.some(
|
|
||||||
(item) => item.uid === permission.uid
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
filteredArray = filteredArray.filter((permission) =>
|
filteredArray = filteredArray.filter((permission) =>
|
||||||
settingFilteredPermissions.some((item) => item.uid === permission.uid)
|
principalTypeFilteredPermissions.some(
|
||||||
|
(item) => item.permissionId === permission.permissionId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
filteredArray = filteredArray.filter((permission) =>
|
||||||
|
settingFilteredPermissions.some(
|
||||||
|
(item) => item.permissionId === permission.permissionId
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
setFilteredPermissions(filteredArray)
|
setFilteredPermissions(filteredArray)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const useUpdatePermissionModal = () => {
|
|||||||
setUpdatePermissionModalOpen(false)
|
setUpdatePermissionModalOpen(false)
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
axios
|
axios
|
||||||
.patch(`/SASjsApi/permission/${selectedPermission?.uid}`, {
|
.patch(`/SASjsApi/permission/${selectedPermission?.permissionId}`, {
|
||||||
setting
|
setting
|
||||||
})
|
})
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
|
|||||||
@@ -26,20 +26,18 @@ const Profile = () => {
|
|||||||
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false)
|
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (appContext.userId) {
|
setIsLoading(true)
|
||||||
setIsLoading(true)
|
axios
|
||||||
axios
|
.get(`/SASjsApi/user/${appContext.userId}`)
|
||||||
.get(`/SASjsApi/user/${appContext.userId}`)
|
.then((res: any) => {
|
||||||
.then((res: any) => {
|
setUser(res.data)
|
||||||
setUser(res.data)
|
})
|
||||||
})
|
.catch((err) => {
|
||||||
.catch((err) => {
|
console.log(err)
|
||||||
console.log(err)
|
})
|
||||||
})
|
.finally(() => {
|
||||||
.finally(() => {
|
setIsLoading(false)
|
||||||
setIsLoading(false)
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [appContext.userId])
|
}, [appContext.userId])
|
||||||
|
|
||||||
const handleChange = (event: any) => {
|
const handleChange = (event: any) => {
|
||||||
|
|||||||
@@ -24,32 +24,39 @@ export enum RunTimeType {
|
|||||||
interface AppContextProps {
|
interface AppContextProps {
|
||||||
checkingSession: boolean
|
checkingSession: boolean
|
||||||
loggedIn: boolean
|
loggedIn: boolean
|
||||||
setLoggedIn?: Dispatch<SetStateAction<boolean>>
|
setLoggedIn: Dispatch<SetStateAction<boolean>> | null
|
||||||
needsToUpdatePassword: boolean
|
needsToUpdatePassword: boolean
|
||||||
setNeedsToUpdatePassword?: Dispatch<SetStateAction<boolean>>
|
setNeedsToUpdatePassword: Dispatch<SetStateAction<boolean>> | null
|
||||||
userId?: string
|
userId: number
|
||||||
setUserId?: Dispatch<SetStateAction<string | undefined>>
|
setUserId: Dispatch<SetStateAction<number>> | null
|
||||||
username: string
|
username: string
|
||||||
setUsername?: Dispatch<SetStateAction<string>>
|
setUsername: Dispatch<SetStateAction<string>> | null
|
||||||
displayName: string
|
displayName: string
|
||||||
setDisplayName?: Dispatch<SetStateAction<string>>
|
setDisplayName: Dispatch<SetStateAction<string>> | null
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
setIsAdmin?: Dispatch<SetStateAction<boolean>>
|
setIsAdmin: Dispatch<SetStateAction<boolean>> | null
|
||||||
mode: ModeType
|
mode: ModeType
|
||||||
runTimes: RunTimeType[]
|
runTimes: RunTimeType[]
|
||||||
logout?: () => void
|
logout: (() => void) | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppContext = createContext<AppContextProps>({
|
export const AppContext = createContext<AppContextProps>({
|
||||||
checkingSession: false,
|
checkingSession: false,
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
|
setLoggedIn: null,
|
||||||
needsToUpdatePassword: false,
|
needsToUpdatePassword: false,
|
||||||
userId: '',
|
setNeedsToUpdatePassword: null,
|
||||||
|
userId: 0,
|
||||||
|
setUserId: null,
|
||||||
username: '',
|
username: '',
|
||||||
|
setUsername: null,
|
||||||
displayName: '',
|
displayName: '',
|
||||||
|
setDisplayName: null,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
|
setIsAdmin: null,
|
||||||
mode: ModeType.Server,
|
mode: ModeType.Server,
|
||||||
runTimes: []
|
runTimes: [],
|
||||||
|
logout: null
|
||||||
})
|
})
|
||||||
|
|
||||||
const AppContextProvider = (props: { children: ReactNode }) => {
|
const AppContextProvider = (props: { children: ReactNode }) => {
|
||||||
@@ -57,7 +64,7 @@ const AppContextProvider = (props: { children: ReactNode }) => {
|
|||||||
const [checkingSession, setCheckingSession] = useState(false)
|
const [checkingSession, setCheckingSession] = useState(false)
|
||||||
const [loggedIn, setLoggedIn] = useState(false)
|
const [loggedIn, setLoggedIn] = useState(false)
|
||||||
const [needsToUpdatePassword, setNeedsToUpdatePassword] = useState(false)
|
const [needsToUpdatePassword, setNeedsToUpdatePassword] = useState(false)
|
||||||
const [userId, setUserId] = useState<string>()
|
const [userId, setUserId] = useState(0)
|
||||||
const [username, setUsername] = useState('')
|
const [username, setUsername] = useState('')
|
||||||
const [displayName, setDisplayName] = useState('')
|
const [displayName, setDisplayName] = useState('')
|
||||||
const [isAdmin, setIsAdmin] = useState(false)
|
const [isAdmin, setIsAdmin] = useState(false)
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ export const findExistingPermission = (
|
|||||||
) => {
|
) => {
|
||||||
for (const permission of existingPermissions) {
|
for (const permission of existingPermissions) {
|
||||||
if (
|
if (
|
||||||
permission.user?.uid === newPermission.principalId &&
|
permission.user?.id === newPermission.principalId &&
|
||||||
hasSameCombination(permission, newPermission)
|
hasSameCombination(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permission.group?.uid === newPermission.principalId &&
|
permission.group?.groupId === newPermission.principalId &&
|
||||||
hasSameCombination(permission, newPermission)
|
hasSameCombination(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
@@ -27,13 +27,13 @@ export const findUpdatingPermission = (
|
|||||||
) => {
|
) => {
|
||||||
for (const permission of existingPermissions) {
|
for (const permission of existingPermissions) {
|
||||||
if (
|
if (
|
||||||
permission.user?.uid === newPermission.principalId &&
|
permission.user?.id === newPermission.principalId &&
|
||||||
hasDifferentSetting(permission, newPermission)
|
hasDifferentSetting(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permission.group?.uid === newPermission.principalId &&
|
permission.group?.groupId === newPermission.principalId &&
|
||||||
hasDifferentSetting(permission, newPermission)
|
hasDifferentSetting(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
export interface UserResponse {
|
export interface UserResponse {
|
||||||
uid: string
|
id: number
|
||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupResponse {
|
export interface GroupResponse {
|
||||||
uid: string
|
groupId: number
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ export interface GroupDetailsResponse extends GroupResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PermissionResponse {
|
export interface PermissionResponse {
|
||||||
uid: string
|
permissionId: number
|
||||||
path: string
|
path: string
|
||||||
type: string
|
type: string
|
||||||
setting: string
|
setting: string
|
||||||
@@ -30,7 +30,7 @@ export interface RegisterPermissionPayload {
|
|||||||
type: string
|
type: string
|
||||||
setting: string
|
setting: string
|
||||||
principalType: string
|
principalType: string
|
||||||
principalId: string
|
principalId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeNode {
|
export interface TreeNode {
|
||||||
|
|||||||
Reference in New Issue
Block a user