mirror of
https://github.com/sasjs/server.git
synced 2026-01-03 13:10:04 +00:00
Compare commits
48 Commits
6690cafbf7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b2a6155a9 | ||
|
|
a56c0b0340 | ||
|
|
02fe79d4d7 | ||
|
|
00a107babd | ||
|
|
4f782025db | ||
|
|
8b5abcd661 | ||
|
|
48e8cb7b2d | ||
|
|
225f381bdf | ||
|
|
3f49186e3b | ||
|
|
ab96653564 | ||
|
|
471c28eaa2 | ||
|
|
584ffe9e0e | ||
|
|
e51b20421a | ||
|
|
631e95604b | ||
|
|
198cd79354 | ||
|
|
379ea604bc | ||
|
|
9ffa403bcb | ||
|
|
6d123c3e23 | ||
|
|
dda1aadc67 | ||
|
|
d47cf15cdb | ||
|
|
d0c7968d66 | ||
|
|
a5c99971cc | ||
|
|
c422e7f02e | ||
|
|
02a993611c | ||
| aca2fff4ac | |||
| af1a386b13 | |||
|
|
f5018ce1df | ||
|
|
3529232f1f | ||
|
|
f4768bffd3 | ||
|
|
c261745f1d | ||
|
|
d6e527ecf2 | ||
|
|
bc2cff1d0d | ||
|
|
66aa9b5891 | ||
|
|
ca17e7c192 | ||
|
|
73df102422 | ||
|
|
48a9a4dd0e | ||
|
|
4f6f735f5b | ||
|
|
6b6546c7ad | ||
|
|
f94ddc0352 | ||
|
|
03670cf0d6 | ||
|
|
ea2ec97c1c | ||
|
|
832f1156e8 | ||
|
|
5cda9cd5d8 | ||
|
|
5d576aff91 | ||
|
|
a044176054 | ||
|
|
deee34f5fd | ||
|
|
b0723f1444 | ||
|
|
e9519cb3c6 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -5,7 +5,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
run: npm run lint-web
|
run: npm run lint-web
|
||||||
|
|
||||||
build-api:
|
build-api:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
CI: true
|
CI: true
|
||||||
|
|
||||||
build-web:
|
build-web:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,5 +1,3 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": ["autoexec", "initialising"]
|
||||||
"autoexec"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
59
CHANGELOG.md
59
CHANGELOG.md
@@ -1,3 +1,62 @@
|
|||||||
|
## [0.39.4](https://github.com/sasjs/server/compare/v0.39.3...v0.39.4) (2025-12-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **deps:** bump dependencies and resolve audit issues ([4f78202](https://github.com/sasjs/server/commit/4f782025dbcdfcbae6ca1fabb42ce1bc385e0162))
|
||||||
|
|
||||||
|
## [0.39.3](https://github.com/sasjs/server/compare/v0.39.2...v0.39.3) (2025-11-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* (deps) bump @sasjs/core to 4.59.7 ([ab96653](https://github.com/sasjs/server/commit/ab966535642d08d4e8e984007b98c8fdffbe30f7))
|
||||||
|
* (deps) rerun npm i to sync ([225f381](https://github.com/sasjs/server/commit/225f381bdf8ad5aa2af8d75648df1dd5175e12e0))
|
||||||
|
|
||||||
|
## [0.39.2](https://github.com/sasjs/server/compare/v0.39.1...v0.39.2) (2025-09-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* addressing test fail ([e51b204](https://github.com/sasjs/server/commit/e51b20421adc1598ea267c79b1fb4dbc085f97b9))
|
||||||
|
* packages missmatch ([379ea60](https://github.com/sasjs/server/commit/379ea604bcb5686b5299fae6a32f759c45b275ea))
|
||||||
|
* type libs ([6d123c3](https://github.com/sasjs/server/commit/6d123c3e23628c1d703eaa13142c77f0da970a55))
|
||||||
|
* typescript errors ([631e956](https://github.com/sasjs/server/commit/631e95604b64b1a96f2abade659348618f3b00b2))
|
||||||
|
* typescript errors ([198cd79](https://github.com/sasjs/server/commit/198cd79354254511c21ac1acfbf7b6bcfdab2af7))
|
||||||
|
|
||||||
|
## [0.39.1](https://github.com/sasjs/server/compare/v0.39.0...v0.39.1) (2025-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* extra bit of sleep for file recognition ([f4768bf](https://github.com/sasjs/server/commit/f4768bffd3dbb2fe243966572ba74002024d96e1)), closes [#381](https://github.com/sasjs/server/issues/381)
|
||||||
|
|
||||||
|
# [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)
|
# [0.36.0](https://github.com/sasjs/server/compare/v0.35.4...v0.36.0) (2024-10-29)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
20538
api/package-lock.json
generated
20538
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,11 +6,9 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"initial": "npm run swagger && npm run compileSysInit && npm run copySASjsCore && npm run downloadMacros",
|
"initial": "npm run swagger && npm run compileSysInit && npm run copySASjsCore && npm run downloadMacros",
|
||||||
"prestart": "npm run initial",
|
"prestart": "npm run initial",
|
||||||
"prebuild": "npm run initial",
|
|
||||||
"start": "NODE_ENV=development nodemon ./src/server.ts",
|
"start": "NODE_ENV=development nodemon ./src/server.ts",
|
||||||
"start:prod": "node ./build/src/server.js",
|
"start:prod": "node ./build/src/server.js",
|
||||||
"build": "rimraf build && tsc",
|
"build": "npm run initial && rimraf build && tsc && npm run copy:files",
|
||||||
"postbuild": "npm run copy:files",
|
|
||||||
"swagger": "tsoa spec",
|
"swagger": "tsoa spec",
|
||||||
"prepare": "[ -d .git ] && git config core.hooksPath ./.git-hooks || true",
|
"prepare": "[ -d .git ] && git config core.hooksPath ./.git-hooks || true",
|
||||||
"test": "mkdir -p tmp && mkdir -p ../web/build && jest --silent --coverage",
|
"test": "mkdir -p tmp && mkdir -p ../web/build && jest --silent --coverage",
|
||||||
@@ -48,25 +46,25 @@
|
|||||||
},
|
},
|
||||||
"author": "4GL Ltd",
|
"author": "4GL Ltd",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/core": "^4.40.1",
|
"@sasjs/core": "^4.59.10",
|
||||||
"@sasjs/utils": "3.2.0",
|
"@sasjs/utils": "^3.5.6",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^5.1.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.21.2",
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.18.2",
|
||||||
"helmet": "^5.0.2",
|
"helmet": "^5.0.2",
|
||||||
"joi": "^17.4.2",
|
"joi": "^17.4.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^9.0.3",
|
||||||
"ldapjs": "2.3.3",
|
"ldapjs": "2.3.3",
|
||||||
"mongoose": "^6.0.12",
|
"mongoose": "^6.13.8",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"rate-limiter-flexible": "2.4.1",
|
"rate-limiter-flexible": "2.4.1",
|
||||||
"rotating-file-stream": "^3.0.4",
|
"rotating-file-stream": "^3.0.4",
|
||||||
"swagger-ui-express": "4.3.0",
|
"swagger-ui-express": "4.3.0",
|
||||||
"unzipper": "^0.10.11",
|
"unzipper": "^0.12.3",
|
||||||
"url": "^0.10.3"
|
"url": "^0.10.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -76,32 +74,32 @@
|
|||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/express": "^4.17.12",
|
"@types/express": "^4.17.12",
|
||||||
"@types/express-session": "^1.17.4",
|
"@types/express-session": "^1.17.4",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^29.5.0",
|
||||||
"@types/jsonwebtoken": "^8.5.5",
|
"@types/jsonwebtoken": "^8.5.5",
|
||||||
"@types/ldapjs": "^2.2.4",
|
"@types/ldapjs": "^2.2.4",
|
||||||
"@types/morgan": "^1.9.3",
|
"@types/morgan": "^1.9.3",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^15.12.2",
|
"@types/node": "^20.0.0",
|
||||||
"@types/supertest": "^2.0.11",
|
"@types/supertest": "^2.0.11",
|
||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
"@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": "^1.12.2",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
"jest": "^27.0.6",
|
"jest": "^29.7.0",
|
||||||
"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": "^3.0.0",
|
||||||
"pkg": "5.6.0",
|
"pkg": "5.6.0",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^3.0.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^29.1.0",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"tsoa": "3.14.1",
|
"tsoa": "3.14.1",
|
||||||
"typescript": "^4.3.2"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
"ignore": [
|
"ignore": [
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -112,8 +113,8 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
sessionId:
|
sessionId:
|
||||||
type: string
|
type: string
|
||||||
description: "The SessionId is the name of the temporary folder used to store the outputs.\nFor SAS, this would be the SASWORK folder. Can be used to poll job status.\nThis session ID should be used to poll job status."
|
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: '{ sessionId: ''20241028074744-54132-1730101664824'' }'
|
example: 20241028074744-54132-1730101664824
|
||||||
required:
|
required:
|
||||||
- sessionId
|
- sessionId
|
||||||
type: object
|
type: object
|
||||||
@@ -314,8 +315,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 +325,7 @@ components:
|
|||||||
isAdmin:
|
isAdmin:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- uid
|
- id
|
||||||
- username
|
- username
|
||||||
- displayName
|
- displayName
|
||||||
- isAdmin
|
- isAdmin
|
||||||
@@ -331,30 +333,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 +366,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 +406,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 +420,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 +489,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 +503,7 @@ components:
|
|||||||
group:
|
group:
|
||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
required:
|
required:
|
||||||
- uid
|
- permissionId
|
||||||
- path
|
- path
|
||||||
- type
|
- type
|
||||||
- setting
|
- setting
|
||||||
@@ -536,8 +542,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,39 +564,35 @@ 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:
|
||||||
@@ -597,6 +601,16 @@ 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:
|
||||||
@@ -1264,7 +1278,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
|
||||||
@@ -1283,7 +1297,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
|
||||||
@@ -1334,7 +1348,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
|
||||||
@@ -1385,7 +1399,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:
|
||||||
@@ -1404,12 +1418,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:
|
||||||
@@ -1421,7 +1437,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
|
||||||
@@ -1435,7 +1451,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
|
||||||
@@ -1461,7 +1478,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
|
||||||
@@ -1486,7 +1504,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
|
||||||
@@ -1505,7 +1523,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
|
||||||
@@ -1521,7 +1539,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
|
||||||
@@ -1543,7 +1561,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:
|
||||||
@@ -1563,11 +1581,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:
|
||||||
@@ -1589,12 +1608,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:
|
||||||
@@ -1606,7 +1626,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
|
||||||
@@ -1617,18 +1637,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:
|
||||||
@@ -1640,8 +1663,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:
|
||||||
@@ -1651,19 +1674,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
|
||||||
@@ -1714,7 +1739,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:
|
||||||
@@ -1734,7 +1759,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
|
||||||
@@ -1748,7 +1773,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:
|
||||||
@@ -1760,7 +1785,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
|
||||||
@@ -1769,11 +1794,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:
|
||||||
@@ -1793,11 +1821,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
|
||||||
@@ -1810,7 +1841,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
|
||||||
@@ -1818,6 +1849,30 @@ 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
|
||||||
@@ -1888,6 +1943,50 @@ 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
|
||||||
@@ -1913,7 +2012,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 {
|
||||||
@@ -234,9 +234,10 @@ const verifyAuthCode = async (
|
|||||||
jwt.verify(code, process.secrets.AUTH_CODE_SECRET, (err, data) => {
|
jwt.verify(code, process.secrets.AUTH_CODE_SECRET, (err, data) => {
|
||||||
if (err) return resolve(undefined)
|
if (err) return resolve(undefined)
|
||||||
|
|
||||||
|
const payload = data as InfoJWT
|
||||||
const clientInfo: InfoJWT = {
|
const clientInfo: InfoJWT = {
|
||||||
clientId: data?.clientId,
|
clientId: payload?.clientId,
|
||||||
userId: data?.userId
|
userId: payload?.userId
|
||||||
}
|
}
|
||||||
if (clientInfo.clientId === clientId) {
|
if (clientInfo.clientId === clientId) {
|
||||||
return resolve(clientInfo)
|
return resolve(clientInfo)
|
||||||
|
|||||||
@@ -42,10 +42,12 @@ interface TriggerCodePayload {
|
|||||||
|
|
||||||
interface TriggerCodeResponse {
|
interface TriggerCodeResponse {
|
||||||
/**
|
/**
|
||||||
* The SessionId is the name of the temporary folder used to store the outputs.
|
* `sessionId` is the ID of the session and the name of the temporary folder
|
||||||
* For SAS, this would be the SASWORK folder. Can be used to poll job status.
|
* used to store code outputs.<br><br>
|
||||||
* This session ID should be used to poll job status.
|
* For SAS, this would be the location of the SASWORK folder.<br><br>
|
||||||
* @example "{ sessionId: '20241028074744-54132-1730101664824' }"
|
* `sessionId` can be used to poll session state using the
|
||||||
|
* GET /SASjsApi/session/{sessionId}/state endpoint.
|
||||||
|
* @example "20241028074744-54132-1730101664824"
|
||||||
*/
|
*/
|
||||||
sessionId: string
|
sessionId: string
|
||||||
}
|
}
|
||||||
@@ -120,7 +122,7 @@ const executeCode = async (
|
|||||||
const triggerCode = async (
|
const triggerCode = async (
|
||||||
req: express.Request,
|
req: express.Request,
|
||||||
{ code, runTime, expiresAfterMins }: TriggerCodePayload
|
{ code, runTime, expiresAfterMins }: TriggerCodePayload
|
||||||
): Promise<{ sessionId: string }> => {
|
): Promise<TriggerCodeResponse> => {
|
||||||
const { user } = req
|
const { user } = req
|
||||||
const userAutoExec =
|
const userAutoExec =
|
||||||
process.env.MODE === ModeType.Server
|
process.env.MODE === ModeType.Server
|
||||||
|
|||||||
@@ -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 } from '../../types'
|
import { PreProgramVars, Session, TreeNode, SessionState } from '../../types'
|
||||||
import {
|
import {
|
||||||
extractHeaders,
|
extractHeaders,
|
||||||
getFilesFolder,
|
getFilesFolder,
|
||||||
@@ -75,8 +75,7 @@ export class ExecutionController {
|
|||||||
|
|
||||||
const session =
|
const session =
|
||||||
sessionByFileUpload ?? (await sessionController.getSession())
|
sessionByFileUpload ?? (await sessionController.getSession())
|
||||||
session.inUse = true
|
session.state = SessionState.running
|
||||||
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')
|
||||||
@@ -121,7 +120,7 @@ export class ExecutionController {
|
|||||||
: ''
|
: ''
|
||||||
|
|
||||||
// it should be deleted by scheduleSessionDestroy
|
// it should be deleted by scheduleSessionDestroy
|
||||||
session.inUse = false
|
session.state = SessionState.completed
|
||||||
|
|
||||||
const resultParts = []
|
const resultParts = []
|
||||||
|
|
||||||
@@ -145,7 +144,9 @@ export class ExecutionController {
|
|||||||
return {
|
return {
|
||||||
httpHeaders,
|
httpHeaders,
|
||||||
result:
|
result:
|
||||||
isDebugOn(vars) || session.crashed ? resultParts.join(`\n`) : webout
|
isDebugOn(vars) || session.failureReason
|
||||||
|
? resultParts.join(`\n`)
|
||||||
|
: webout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ 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 {
|
import { executeProgramRawValidation, getRunTimeAndFilePath } from '../../utils'
|
||||||
executeProgramRawValidation,
|
import { SessionState } from '../../types'
|
||||||
getRunTimeAndFilePath,
|
|
||||||
RunTimeType
|
|
||||||
} from '../../utils'
|
|
||||||
|
|
||||||
export class FileUploadController {
|
export class FileUploadController {
|
||||||
private storage = multer.diskStorage({
|
private storage = multer.diskStorage({
|
||||||
@@ -56,9 +53,8 @@ export class FileUploadController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const session = await sessionController.getSession()
|
const session = await sessionController.getSession()
|
||||||
// marking consumed true, so that it's not available
|
// change session state to 'running', so that it's not available for any other request
|
||||||
// as readySession for any other request
|
session.state = SessionState.running
|
||||||
session.consumed = true
|
|
||||||
|
|
||||||
req.sasjsSession = session
|
req.sasjsSession = session
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { Session } from '../../types'
|
import { Session, SessionState } from '../../types'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { execFile } from 'child_process'
|
import { execFile } from 'child_process'
|
||||||
import {
|
import {
|
||||||
@@ -23,7 +23,9 @@ export class SessionController {
|
|||||||
protected sessions: Session[] = []
|
protected sessions: Session[] = []
|
||||||
|
|
||||||
protected getReadySessions = (): Session[] =>
|
protected getReadySessions = (): Session[] =>
|
||||||
this.sessions.filter((sess: Session) => sess.ready && !sess.consumed)
|
this.sessions.filter(
|
||||||
|
(session: Session) => session.state === SessionState.pending
|
||||||
|
)
|
||||||
|
|
||||||
protected async createSession(): Promise<Session> {
|
protected async createSession(): Promise<Session> {
|
||||||
const sessionId = generateUniqueFileName(generateTimestamp())
|
const sessionId = generateUniqueFileName(generateTimestamp())
|
||||||
@@ -39,19 +41,18 @@ export class SessionController {
|
|||||||
|
|
||||||
const session: Session = {
|
const session: Session = {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
ready: true,
|
state: SessionState.pending,
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +67,10 @@ 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 {
|
||||||
@@ -83,10 +88,7 @@ export class SASSessionController extends SessionController {
|
|||||||
|
|
||||||
const session: Session = {
|
const session: Session = {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
ready: false,
|
state: SessionState.initialising,
|
||||||
inUse: false,
|
|
||||||
consumed: false,
|
|
||||||
completed: false,
|
|
||||||
creationTimeStamp,
|
creationTimeStamp,
|
||||||
deathTimeStamp,
|
deathTimeStamp,
|
||||||
path: sessionFolder
|
path: sessionFolder
|
||||||
@@ -144,13 +146,20 @@ ${autoExecContent}`
|
|||||||
process.sasLoc!.endsWith('sas.exe') ? session.path : ''
|
process.sasLoc!.endsWith('sas.exe') ? session.path : ''
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
session.completed = true
|
session.state = SessionState.completed
|
||||||
|
|
||||||
process.logger.info('session completed', session)
|
process.logger.info('session completed', session)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
session.completed = true
|
session.state = SessionState.failed
|
||||||
session.crashed = err.toString()
|
|
||||||
process.logger.error('session crashed', session.id, session.crashed)
|
session.failureReason = err.toString()
|
||||||
|
|
||||||
|
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
|
||||||
@@ -167,15 +176,19 @@ ${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 ((await fileExists(codeFilePath)) && !session.crashed) {}
|
while (
|
||||||
|
(await fileExists(codeFilePath)) &&
|
||||||
|
session.state !== SessionState.failed
|
||||||
|
) {}
|
||||||
|
|
||||||
if (session.crashed)
|
if (session.state === SessionState.failed) {
|
||||||
process.logger.error(
|
process.logger.error(
|
||||||
'session crashed! while waiting to be ready',
|
'session crashed! while waiting to be ready',
|
||||||
session.crashed
|
session.failureReason
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
session.ready = true
|
session.state = SessionState.pending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteSession(session: Session) {
|
private async deleteSession(session: Session) {
|
||||||
@@ -191,7 +204,7 @@ ${autoExecContent}`
|
|||||||
private scheduleSessionDestroy(session: Session) {
|
private scheduleSessionDestroy(session: Session) {
|
||||||
setTimeout(
|
setTimeout(
|
||||||
async () => {
|
async () => {
|
||||||
if (session.inUse) {
|
if (session.state === SessionState.running) {
|
||||||
// adding 10 more minutes
|
// adding 10 more minutes
|
||||||
const newDeathTimeStamp =
|
const newDeathTimeStamp =
|
||||||
parseInt(session.deathTimeStamp) + 10 * 60 * 1000
|
parseInt(session.deathTimeStamp) + 10 * 60 * 1000
|
||||||
@@ -202,7 +215,7 @@ ${autoExecContent}`
|
|||||||
const { expiresAfterMins } = session
|
const { expiresAfterMins } = session
|
||||||
|
|
||||||
// delay session destroy if expiresAfterMins present
|
// delay session destroy if expiresAfterMins present
|
||||||
if (expiresAfterMins && !expiresAfterMins.used) {
|
if (expiresAfterMins && session.state !== SessionState.completed) {
|
||||||
// calculate session death time using expiresAfterMins
|
// calculate session death time using expiresAfterMins
|
||||||
const newDeathTimeStamp =
|
const newDeathTimeStamp =
|
||||||
parseInt(session.deathTimeStamp) +
|
parseInt(session.deathTimeStamp) +
|
||||||
@@ -247,9 +260,16 @@ data _null_;
|
|||||||
rc=filename(fname,getoption('SYSIN') );
|
rc=filename(fname,getoption('SYSIN') );
|
||||||
if rc = 0 and fexist(fname) then rc=fdelete(fname);
|
if rc = 0 and fexist(fname) then rc=fdelete(fname);
|
||||||
rc=filename(fname);
|
rc=filename(fname);
|
||||||
/* now wait for the real SYSIN */
|
/* now wait for the real SYSIN (location of code.sas) */
|
||||||
slept=0;
|
slept=0;fname='';
|
||||||
do until ( fileexist(getoption('SYSIN')) or slept>(60*15) );
|
do until (slept>(60*15));
|
||||||
|
rc=filename(fname,getoption('SYSIN'));
|
||||||
|
if rc = 0 and fexist(fname) then do;
|
||||||
|
putlog fname=;
|
||||||
|
rc=filename(fname);
|
||||||
|
rc=sleep(0.01,1); /* wait just a little more */
|
||||||
|
stop;
|
||||||
|
end;
|
||||||
slept=slept+sleep(0.01,1);
|
slept=slept+sleep(0.01,1);
|
||||||
end;
|
end;
|
||||||
stop;
|
stop;
|
||||||
|
|||||||
@@ -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 } from '../../types'
|
import { PreProgramVars, Session, SessionState } 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.completed) {
|
while (session.state !== SessionState.completed) {
|
||||||
await delay(50)
|
await delay(50)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -114,13 +114,20 @@ export const processProgram = async (
|
|||||||
|
|
||||||
await execFilePromise(executablePath, [codePath], writeStream)
|
await execFilePromise(executablePath, [codePath], writeStream)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
session.completed = true
|
session.state = SessionState.completed
|
||||||
|
|
||||||
process.logger.info('session completed', session)
|
process.logger.info('session completed', session)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
session.completed = true
|
session.state = SessionState.failed
|
||||||
session.crashed = err.toString()
|
|
||||||
process.logger.error('session crashed', session.id, session.crashed)
|
session.failureReason = err.toString()
|
||||||
|
|
||||||
|
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,10 +1,11 @@
|
|||||||
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')
|
||||||
@@ -15,12 +16,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(
|
||||||
@@ -28,6 +28,18 @@ 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) => ({
|
||||||
@@ -37,3 +49,23 @@ 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,13 +1,16 @@
|
|||||||
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 { ExecutionController, ExecutionVars } from './internal'
|
import {
|
||||||
|
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 {
|
||||||
/**
|
/**
|
||||||
@@ -17,6 +20,36 @@ 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')
|
||||||
@@ -79,6 +112,26 @@ 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 (
|
||||||
@@ -117,3 +170,52 @@ 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,7 +280,7 @@ 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,
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,10 @@ const login = async (
|
|||||||
const rateLimiter = RateLimiter.getInstance()
|
const rateLimiter = RateLimiter.getInstance()
|
||||||
|
|
||||||
if (!validPass) {
|
if (!validPass) {
|
||||||
const retrySecs = await rateLimiter.consume(req.ip, user?.username)
|
const retrySecs = await rateLimiter.consume(
|
||||||
|
req.ip || 'unknown',
|
||||||
|
user?.username
|
||||||
|
)
|
||||||
if (retrySecs > 0) throw errors.tooManyRequests(retrySecs)
|
if (retrySecs > 0) throw errors.tooManyRequests(retrySecs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +117,7 @@ const login = async (
|
|||||||
if (!validPass) throw errors.invalidPassword
|
if (!validPass) throw errors.invalidPassword
|
||||||
|
|
||||||
// Reset on successful authorization
|
// Reset on successful authorization
|
||||||
rateLimiter.resetOnSuccess(req.ip, user.username)
|
rateLimiter.resetOnSuccess(req.ip || 'unknown', user.username)
|
||||||
|
|
||||||
req.session.loggedIn = true
|
req.session.loggedIn = true
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ export const authenticateAccessToken: RequestHandler = async (
|
|||||||
if (user.isActive) {
|
if (user.isActive) {
|
||||||
req.user = user
|
req.user = user
|
||||||
return csrfProtection(req, res, nextFunction)
|
return csrfProtection(req, res, nextFunction)
|
||||||
} else return res.sendStatus(401)
|
} else return res.status(401).send('Unauthorized')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res.sendStatus(401)
|
return res.status(401).send('Unauthorized')
|
||||||
}
|
}
|
||||||
|
|
||||||
await authenticateToken(
|
await authenticateToken(
|
||||||
@@ -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',
|
||||||
@@ -118,6 +118,6 @@ const authenticateToken = async (
|
|||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sendStatus(401)
|
res.status(401).send('Unauthorized')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { convertSecondsToHms } from '@sasjs/utils'
|
|||||||
import { RateLimiter } from '../utils'
|
import { RateLimiter } from '../utils'
|
||||||
|
|
||||||
export const bruteForceProtection: RequestHandler = async (req, res, next) => {
|
export const bruteForceProtection: RequestHandler = async (req, res, next) => {
|
||||||
const ip = req.ip
|
const ip = req.ip || 'unknown'
|
||||||
const username = req.body.username
|
const username = req.body.username
|
||||||
|
|
||||||
const rateLimiter = RateLimiter.getInstance()
|
const rateLimiter = RateLimiter.getInstance()
|
||||||
|
|||||||
@@ -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,23 +37,16 @@ interface IGroup extends IGroupDocument {
|
|||||||
}
|
}
|
||||||
interface IGroupModel extends Model<IGroup> {}
|
interface IGroupModel extends Model<IGroup> {}
|
||||||
|
|
||||||
const opts = {
|
const groupSchema = new Schema<IGroupDocument>({
|
||||||
toJSON: {
|
|
||||||
virtuals: true,
|
|
||||||
transform: function (doc: any, ret: any, options: any) {
|
|
||||||
delete ret._id
|
|
||||||
delete ret.id
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const groupSchema = new Schema<IGroupDocument>(
|
|
||||||
{
|
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
unique: true
|
unique: true
|
||||||
},
|
},
|
||||||
|
groupId: {
|
||||||
|
type: Number,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Group description.'
|
default: 'Group description.'
|
||||||
@@ -69,22 +60,23 @@ const groupSchema = new Schema<IGroupDocument>(
|
|||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
|
users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
|
||||||
},
|
})
|
||||||
opts
|
|
||||||
)
|
|
||||||
|
|
||||||
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()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// pre remove hook to remove all references of group from users
|
// pre remove hook to remove all references of group from users
|
||||||
groupSchema.pre('remove', async function () {
|
groupSchema.pre('remove', async function (this: IGroupDocument) {
|
||||||
const userIds = this.users
|
const userIds = this.users
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
userIds.map(async (userId) => {
|
userIds.map(async (userId) => {
|
||||||
|
|||||||
@@ -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,19 +22,11 @@ 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: {
|
path: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
@@ -50,12 +41,13 @@ const permissionSchema = new Schema<IPermissionDocument>(
|
|||||||
},
|
},
|
||||||
user: { type: Schema.Types.ObjectId, ref: 'User' },
|
user: { type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
group: { type: Schema.Types.ObjectId, ref: 'Group' }
|
group: { type: Schema.Types.ObjectId, ref: 'Group' }
|
||||||
},
|
})
|
||||||
opts
|
|
||||||
)
|
|
||||||
|
|
||||||
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,19 +54,8 @@ 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
|
||||||
@@ -78,6 +65,10 @@ const userSchema = new Schema<IUserDocument>(
|
|||||||
required: true,
|
required: true,
|
||||||
unique: true
|
unique: true
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
password: {
|
password: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
@@ -118,12 +109,15 @@ const userSchema = new Schema<IUserDocument>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
})
|
||||||
opts
|
|
||||||
)
|
|
||||||
|
|
||||||
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,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(uid)
|
await controller.deletePermission(permissionId)
|
||||||
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,16 +1,37 @@
|
|||||||
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()
|
||||||
|
|
||||||
sessionRouter.get('/', async (req, res) => {
|
|
||||||
const controller = new SessionController()
|
const controller = new SessionController()
|
||||||
|
|
||||||
|
sessionRouter.get('/', async (req, res) => {
|
||||||
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 } from '../../../types'
|
import { Session, SessionState } 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,10 +493,7 @@ const mockedGetSession = async () => {
|
|||||||
|
|
||||||
const session: Session = {
|
const session: Session = {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
ready: true,
|
state: SessionState.pending,
|
||||||
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,
|
||||||
@@ -277,7 +277,10 @@ const performLogin = async (
|
|||||||
.set('x-xsrf-token', csrfToken)
|
.set('x-xsrf-token', csrfToken)
|
||||||
.send(credentials)
|
.send(credentials)
|
||||||
|
|
||||||
return { authCookies: header['set-cookie'].join() }
|
return {
|
||||||
|
authCookies:
|
||||||
|
(header['set-cookie'] as unknown as string[] | undefined)?.join() || ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractCSRF = (text: string) =>
|
const extractCSRF = (text: string) =>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { executeProgramRawValidation } from '../../utils'
|
import {
|
||||||
|
executeProgramRawValidation,
|
||||||
|
triggerProgramValidation
|
||||||
|
} from '../../utils'
|
||||||
import { STPController } from '../../controllers/'
|
import { STPController } from '../../controllers/'
|
||||||
import { FileUploadController } from '../../controllers/internal'
|
import { FileUploadController } from '../../controllers/internal'
|
||||||
|
|
||||||
@@ -69,4 +72,28 @@ 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,12 +1,16 @@
|
|||||||
|
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
|
||||||
ready: boolean
|
state: SessionState
|
||||||
creationTimeStamp: string
|
creationTimeStamp: string
|
||||||
deathTimeStamp: string
|
deathTimeStamp: string
|
||||||
path: string
|
path: string
|
||||||
inUse: boolean
|
|
||||||
consumed: boolean
|
|
||||||
completed: boolean
|
|
||||||
crashed?: string
|
|
||||||
expiresAfterMins?: { mins: number; used: boolean }
|
expiresAfterMins?: { mins: number; used: boolean }
|
||||||
|
failureReason?: 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
|
||||||
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import User from '../model/User'
|
import User from '../model/User'
|
||||||
|
import { InfoJWT } from '../types/InfoJWT'
|
||||||
|
|
||||||
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) =>
|
||||||
jwt.verify(token, key, (err, decoded) => {
|
jwt.verify(token, key, (err, decoded) => {
|
||||||
if (err) return reject(false)
|
if (err) return reject(false)
|
||||||
|
|
||||||
if (decoded?.userId === userId && decoded?.clientId === clientId) {
|
const payload = decoded as InfoJWT
|
||||||
|
if (payload?.userId === userId && payload?.clientId === clientId) {
|
||||||
return resolve(true)
|
return resolve(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +24,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'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 =>
|
||||||
@@ -197,3 +192,17 @@ 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
|
||||||
|
|
||||||
|
|||||||
12860
package-lock.json
generated
12860
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
3401
web/package-lock.json
generated
3401
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,8 @@
|
|||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
"@types/node": "^12.20.28",
|
"@types/node": "^12.20.28",
|
||||||
"@types/react": "^17.0.27",
|
"@types/react": "^17.0.27",
|
||||||
"axios": "^0.24.0",
|
"axios": "^1.12.2",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
@@ -54,6 +53,7 @@
|
|||||||
"eslint-webpack-plugin": "^3.1.1",
|
"eslint-webpack-plugin": "^3.1.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-webpack-plugin": "5.5.0",
|
"html-webpack-plugin": "5.5.0",
|
||||||
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.44.0",
|
||||||
|
|||||||
@@ -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) =>
|
filteredArray = filteredArray.filter((permission) =>
|
||||||
principalTypeFilteredPermissions.some(
|
principalTypeFilteredPermissions.some(
|
||||||
(item) => item.uid === permission.uid
|
(item) => item.permissionId === permission.permissionId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
filteredArray = filteredArray.filter((permission) =>
|
filteredArray = filteredArray.filter((permission) =>
|
||||||
settingFilteredPermissions.some((item) => item.uid === permission.uid)
|
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,7 +26,6 @@ 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}`)
|
||||||
@@ -39,7 +38,6 @@ const Profile = () => {
|
|||||||
.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)
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family:
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||||
sans-serif;
|
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family:
|
||||||
monospace;
|
source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|||||||
@@ -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