mirror of
https://github.com/sasjs/server.git
synced 2025-12-11 03:34:35 +00:00
Compare commits
33 Commits
v0.35.0
...
6690cafbf7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6690cafbf7 | ||
|
|
0838b8112e | ||
|
|
441f8b7726 | ||
|
|
049a7f4b80 | ||
|
|
3053c68bdf | ||
|
|
76750e864d | ||
|
|
ffcf193b87 | ||
|
|
aa2a1cbe13 | ||
|
|
6f2c53555c | ||
|
|
73d965daf5 | ||
|
|
4f1763db67 | ||
|
|
28222add04 | ||
|
|
068edfd6a5 | ||
|
|
7e8cbbf377 | ||
| c43afabe28 | |||
| 1531e9cd9c | |||
|
|
1fc1431442 | ||
|
|
3387efbb9a | ||
|
|
e2996b495f | ||
|
|
41c627f93a | ||
|
|
49f5dc7555 | ||
|
|
f6e77f99a4 | ||
|
|
b57dfa429b | ||
| 9586dbb2d0 | |||
|
|
a4f78ab48d | ||
|
|
2f47a2213b | ||
|
|
0f91395fbb | ||
|
|
167b14fed0 | ||
|
|
8940f4dc47 | ||
| 8cdf605006 | |||
| 3f815e9beb | |||
| 6c88eeabd2 | |||
| 093fe90589 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -56,4 +56,4 @@ jobs:
|
|||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
run: |
|
run: |
|
||||||
GITHUB_TOKEN=${{ secrets.GH_TOKEN }} semantic-release
|
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} semantic-release
|
||||||
|
|||||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,3 +1,38 @@
|
|||||||
|
# [0.36.0](https://github.com/sasjs/server/compare/v0.35.4...v0.36.0) (2024-10-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **code:** added code/trigger API endpoint ([ffcf193](https://github.com/sasjs/server/commit/ffcf193b87d811b166d79af74013776a253b50b0))
|
||||||
|
|
||||||
|
## [0.35.4](https://github.com/sasjs/server/compare/v0.35.3...v0.35.4) (2024-01-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **api:** fixed env issue in MacOS executable ([73d965d](https://github.com/sasjs/server/commit/73d965daf54b16c0921e4b18d11a1e6f8650884d))
|
||||||
|
|
||||||
|
## [0.35.3](https://github.com/sasjs/server/compare/v0.35.2...v0.35.3) (2023-11-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable embedded LFs in JS STP vars ([7e8cbbf](https://github.com/sasjs/server/commit/7e8cbbf377b27a7f5dd9af0bc6605c01f302f5d9))
|
||||||
|
|
||||||
|
## [0.35.2](https://github.com/sasjs/server/compare/v0.35.1...v0.35.2) (2023-08-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add _debug as optional query param in swagger apis for GET stp/execute ([9586dbb](https://github.com/sasjs/server/commit/9586dbb2d0d6611061c9efdfb84030144f62c2ee))
|
||||||
|
|
||||||
|
## [0.35.1](https://github.com/sasjs/server/compare/v0.35.0...v0.35.1) (2023-07-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **log-separator:** log separator should always wrap log ([8940f4d](https://github.com/sasjs/server/commit/8940f4dc47abae2036b4fcdeb772c31a0ca07cca))
|
||||||
|
|
||||||
# [0.35.0](https://github.com/sasjs/server/compare/v0.34.2...v0.35.0) (2023-05-03)
|
# [0.35.0](https://github.com/sasjs/server/compare/v0.34.2...v0.35.0) (2023-05-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
16
api/package-lock.json
generated
16
api/package-lock.json
generated
@@ -51,7 +51,7 @@
|
|||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
"axios": "0.27.2",
|
"axios": "0.27.2",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^16.0.1",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
"mongodb-memory-server": "8.11.4",
|
"mongodb-memory-server": "8.11.4",
|
||||||
@@ -4788,12 +4788,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "10.0.0",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
||||||
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/duplexer2": {
|
"node_modules/duplexer2": {
|
||||||
@@ -15217,9 +15217,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dotenv": {
|
"dotenv": {
|
||||||
"version": "10.0.0",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
||||||
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"duplexer2": {
|
"duplexer2": {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
"axios": "0.27.2",
|
"axios": "0.27.2",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^16.0.1",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
"mongodb-memory-server": "8.11.4",
|
"mongodb-memory-server": "8.11.4",
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ components:
|
|||||||
clientId:
|
clientId:
|
||||||
type: string
|
type: string
|
||||||
userId:
|
userId:
|
||||||
type: number
|
type: string
|
||||||
format: double
|
|
||||||
required:
|
required:
|
||||||
- clientId
|
- clientId
|
||||||
- userId
|
- userId
|
||||||
@@ -98,17 +97,47 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
code:
|
code:
|
||||||
type: string
|
type: string
|
||||||
description: 'Code of program'
|
description: 'The code to be executed'
|
||||||
example: '* Code HERE;'
|
example: '* Your Code HERE;'
|
||||||
runTime:
|
runTime:
|
||||||
$ref: '#/components/schemas/RunTimeType'
|
$ref: '#/components/schemas/RunTimeType'
|
||||||
description: 'runtime for program'
|
description: 'The runtime for the code - eg SAS, JS, PY or R'
|
||||||
example: js
|
example: js
|
||||||
required:
|
required:
|
||||||
- code
|
- code
|
||||||
- runTime
|
- runTime
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
TriggerCodeResponse:
|
||||||
|
properties:
|
||||||
|
sessionId:
|
||||||
|
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."
|
||||||
|
example: '{ sessionId: ''20241028074744-54132-1730101664824'' }'
|
||||||
|
required:
|
||||||
|
- sessionId
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
TriggerCodePayload:
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
description: 'The code to be executed'
|
||||||
|
example: '* Your Code HERE;'
|
||||||
|
runTime:
|
||||||
|
$ref: '#/components/schemas/RunTimeType'
|
||||||
|
description: 'The runtime for the code - eg SAS, JS, PY or R'
|
||||||
|
example: sas
|
||||||
|
expiresAfterMins:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
description: "Amount of minutes after the completion of the job when the session must be\ndestroyed."
|
||||||
|
example: 15
|
||||||
|
required:
|
||||||
|
- code
|
||||||
|
- runTime
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
MemberType.folder:
|
MemberType.folder:
|
||||||
enum:
|
enum:
|
||||||
- folder
|
- folder
|
||||||
@@ -285,9 +314,8 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
UserResponse:
|
UserResponse:
|
||||||
properties:
|
properties:
|
||||||
id:
|
uid:
|
||||||
type: number
|
type: string
|
||||||
format: double
|
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
displayName:
|
displayName:
|
||||||
@@ -295,7 +323,7 @@ components:
|
|||||||
isAdmin:
|
isAdmin:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- id
|
- uid
|
||||||
- username
|
- username
|
||||||
- displayName
|
- displayName
|
||||||
- isAdmin
|
- isAdmin
|
||||||
@@ -303,32 +331,30 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
GroupResponse:
|
GroupResponse:
|
||||||
properties:
|
properties:
|
||||||
groupId:
|
uid:
|
||||||
type: number
|
type: string
|
||||||
format: double
|
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- groupId
|
- uid
|
||||||
- name
|
- name
|
||||||
- description
|
- description
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
UserDetailsResponse:
|
UserDetailsResponse:
|
||||||
properties:
|
properties:
|
||||||
id:
|
uid:
|
||||||
type: number
|
|
||||||
format: double
|
|
||||||
displayName:
|
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
isActive:
|
displayName:
|
||||||
type: boolean
|
type: string
|
||||||
isAdmin:
|
isAdmin:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
isActive:
|
||||||
|
type: boolean
|
||||||
autoExec:
|
autoExec:
|
||||||
type: string
|
type: string
|
||||||
groups:
|
groups:
|
||||||
@@ -336,11 +362,11 @@ components:
|
|||||||
$ref: '#/components/schemas/GroupResponse'
|
$ref: '#/components/schemas/GroupResponse'
|
||||||
type: array
|
type: array
|
||||||
required:
|
required:
|
||||||
- id
|
- uid
|
||||||
- displayName
|
|
||||||
- username
|
- username
|
||||||
- isActive
|
- displayName
|
||||||
- isAdmin
|
- isAdmin
|
||||||
|
- isActive
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
UserPayload:
|
UserPayload:
|
||||||
@@ -376,9 +402,8 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
GroupDetailsResponse:
|
GroupDetailsResponse:
|
||||||
properties:
|
properties:
|
||||||
groupId:
|
uid:
|
||||||
type: number
|
type: string
|
||||||
format: double
|
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
@@ -390,7 +415,7 @@ components:
|
|||||||
$ref: '#/components/schemas/UserResponse'
|
$ref: '#/components/schemas/UserResponse'
|
||||||
type: array
|
type: array
|
||||||
required:
|
required:
|
||||||
- groupId
|
- uid
|
||||||
- name
|
- name
|
||||||
- description
|
- description
|
||||||
- isActive
|
- isActive
|
||||||
@@ -459,9 +484,8 @@ components:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
PermissionDetailsResponse:
|
PermissionDetailsResponse:
|
||||||
properties:
|
properties:
|
||||||
permissionId:
|
uid:
|
||||||
type: number
|
type: string
|
||||||
format: double
|
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
@@ -473,7 +497,7 @@ components:
|
|||||||
group:
|
group:
|
||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
required:
|
required:
|
||||||
- permissionId
|
- uid
|
||||||
- path
|
- path
|
||||||
- type
|
- type
|
||||||
- setting
|
- setting
|
||||||
@@ -512,10 +536,8 @@ components:
|
|||||||
description: 'Indicates the type of principal'
|
description: 'Indicates the type of principal'
|
||||||
example: user
|
example: user
|
||||||
principalId:
|
principalId:
|
||||||
type: number
|
type: string
|
||||||
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
|
||||||
@@ -534,25 +556,37 @@ components:
|
|||||||
- setting
|
- setting
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
SessionResponse:
|
Pick_UserResponse.Exclude_keyofUserResponse.uid__:
|
||||||
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
|
||||||
needsToUpdatePassword:
|
|
||||||
type: boolean
|
|
||||||
required:
|
required:
|
||||||
- id
|
|
||||||
- username
|
- username
|
||||||
- displayName
|
- displayName
|
||||||
- isAdmin
|
- isAdmin
|
||||||
- needsToUpdatePassword
|
type: object
|
||||||
|
description: 'From T, pick a set of properties whose keys are in the union K'
|
||||||
|
SessionResponse:
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
|
isAdmin:
|
||||||
|
type: boolean
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
needsToUpdatePassword:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- username
|
||||||
|
- displayName
|
||||||
|
- isAdmin
|
||||||
|
- id
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
ExecutePostRequestPayload:
|
ExecutePostRequestPayload:
|
||||||
@@ -792,7 +826,7 @@ paths:
|
|||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: 'Execute Code on the Specified Runtime'
|
description: 'Execute Code on the Specified Runtime'
|
||||||
summary: 'Run Code and Return Webout Content and Log'
|
summary: "Run Code and Return Webout Content, Log and Print output\nThe order of returned parts of the payload is:\n1. Webout (if present)\n2. Logs UUID (used as separator)\n3. Log\n4. Logs UUID (used as separator)\n5. Print (if present and if the runtime is SAS)\nPlease see"
|
||||||
tags:
|
tags:
|
||||||
- Code
|
- Code
|
||||||
security:
|
security:
|
||||||
@@ -805,6 +839,30 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ExecuteCodePayload'
|
$ref: '#/components/schemas/ExecuteCodePayload'
|
||||||
|
/SASjsApi/code/trigger:
|
||||||
|
post:
|
||||||
|
operationId: TriggerCode
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TriggerCodeResponse'
|
||||||
|
description: 'Trigger Code on the Specified Runtime'
|
||||||
|
summary: 'Triggers code and returns SessionId immediately - does not wait for job completion'
|
||||||
|
tags:
|
||||||
|
- Code
|
||||||
|
security:
|
||||||
|
-
|
||||||
|
bearerAuth: []
|
||||||
|
parameters: []
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TriggerCodePayload'
|
||||||
/SASjsApi/drive/deploy:
|
/SASjsApi/drive/deploy:
|
||||||
post:
|
post:
|
||||||
operationId: Deploy
|
operationId: Deploy
|
||||||
@@ -1206,7 +1264,7 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: [{id: 123, username: johnusername, displayName: John, isAdmin: false}, {id: 456, username: starkusername, displayName: Stark, isAdmin: true}]
|
value: [{uid: userIdString, username: johnusername, displayName: John, isAdmin: false}, {uid: anotherUserIdString, 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
|
||||||
@@ -1225,7 +1283,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/UserDetailsResponse'
|
$ref: '#/components/schemas/UserDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
value: {uid: userIdString, 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
|
||||||
@@ -1276,7 +1334,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/UserDetailsResponse'
|
$ref: '#/components/schemas/UserDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
value: {uid: userIdString, 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
|
||||||
@@ -1327,7 +1385,7 @@ paths:
|
|||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
'/SASjsApi/user/{userId}':
|
'/SASjsApi/user/{uid}':
|
||||||
get:
|
get:
|
||||||
operationId: GetUser
|
operationId: GetUser
|
||||||
responses:
|
responses:
|
||||||
@@ -1346,14 +1404,12 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'The user''s identifier'
|
|
||||||
in: path
|
in: path
|
||||||
name: userId
|
name: uid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
'/SASjsApi/user/{userId}':
|
||||||
example: 1234
|
|
||||||
patch:
|
patch:
|
||||||
operationId: UpdateUser
|
operationId: UpdateUser
|
||||||
responses:
|
responses:
|
||||||
@@ -1365,7 +1421,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/UserDetailsResponse'
|
$ref: '#/components/schemas/UserDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
value: {uid: userIdString, 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
|
||||||
@@ -1379,8 +1435,7 @@ paths:
|
|||||||
name: userId
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
|
||||||
example: '1234'
|
example: '1234'
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
@@ -1406,8 +1461,7 @@ paths:
|
|||||||
name: userId
|
name: userId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
|
||||||
example: 1234
|
example: 1234
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
@@ -1432,7 +1486,7 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: [{groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users'}]
|
value: [{uid: groupIdString, 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
|
||||||
@@ -1451,7 +1505,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
value: {uid: groupIdString, 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
|
||||||
@@ -1467,7 +1521,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupPayload'
|
$ref: '#/components/schemas/GroupPayload'
|
||||||
'/SASjsApi/group/by/groupname/{name}':
|
'/SASjsApi/group/by/groupname/{name}':
|
||||||
get:
|
get:
|
||||||
operationId: GetGroupByGroupName
|
operationId: GetGroupByName
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Ok
|
description: Ok
|
||||||
@@ -1489,7 +1543,7 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
'/SASjsApi/group/{groupId}':
|
'/SASjsApi/group/{uid}':
|
||||||
get:
|
get:
|
||||||
operationId: GetGroup
|
operationId: GetGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1509,12 +1563,11 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: groupId
|
name: uid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
example: 12ByteString
|
||||||
example: 1234
|
|
||||||
delete:
|
delete:
|
||||||
operationId: DeleteGroup
|
operationId: DeleteGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1536,13 +1589,12 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: groupId
|
name: uid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
example: 12ByteString
|
||||||
example: 1234
|
'/SASjsApi/group/{groupUid}/{userUid}':
|
||||||
'/SASjsApi/group/{groupId}/{userId}':
|
|
||||||
post:
|
post:
|
||||||
operationId: AddUserToGroup
|
operationId: AddUserToGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1554,7 +1606,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
value: {uid: groupIdString, 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
|
||||||
@@ -1565,21 +1617,18 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: groupId
|
name: groupUid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
example: 12ByteString
|
||||||
example: '1234'
|
|
||||||
-
|
-
|
||||||
description: 'The user''s identifier'
|
description: 'The user''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: userId
|
name: userUid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
|
||||||
example: '6789'
|
|
||||||
delete:
|
delete:
|
||||||
operationId: RemoveUserFromGroup
|
operationId: RemoveUserFromGroup
|
||||||
responses:
|
responses:
|
||||||
@@ -1591,8 +1640,8 @@ paths:
|
|||||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
value: {uid: groupIdString, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||||
summary: 'Remove a user to a group. Admin task only.'
|
summary: 'Remove a user from a group. Admin task only.'
|
||||||
tags:
|
tags:
|
||||||
- Group
|
- Group
|
||||||
security:
|
security:
|
||||||
@@ -1602,21 +1651,19 @@ paths:
|
|||||||
-
|
-
|
||||||
description: 'The group''s identifier'
|
description: 'The group''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: groupId
|
name: groupUid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
example: 12ByteString
|
||||||
example: '1234'
|
|
||||||
-
|
-
|
||||||
description: 'The user''s identifier'
|
description: 'The user''s identifier'
|
||||||
in: path
|
in: path
|
||||||
name: userId
|
name: userUid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
example: 12ByteString
|
||||||
example: '6789'
|
|
||||||
/SASjsApi/info:
|
/SASjsApi/info:
|
||||||
get:
|
get:
|
||||||
operationId: Info
|
operationId: Info
|
||||||
@@ -1667,7 +1714,7 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
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: []}}]
|
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: []}}]
|
||||||
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:
|
||||||
@@ -1687,7 +1734,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/PermissionDetailsResponse'
|
$ref: '#/components/schemas/PermissionDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
value: {uid: permissionIdString, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {uid: userIdString, 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
|
||||||
@@ -1701,7 +1748,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RegisterPermissionPayload'
|
$ref: '#/components/schemas/RegisterPermissionPayload'
|
||||||
'/SASjsApi/permission/{permissionId}':
|
'/SASjsApi/permission/{uid}':
|
||||||
patch:
|
patch:
|
||||||
operationId: UpdatePermission
|
operationId: UpdatePermission
|
||||||
responses:
|
responses:
|
||||||
@@ -1713,7 +1760,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/PermissionDetailsResponse'
|
$ref: '#/components/schemas/PermissionDetailsResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
value: {uid: permissionIdString, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {uid: userIdString, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
||||||
summary: 'Update permission setting. Admin only'
|
summary: 'Update permission setting. Admin only'
|
||||||
tags:
|
tags:
|
||||||
- Permission
|
- Permission
|
||||||
@@ -1722,14 +1769,11 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'The permission''s identifier'
|
|
||||||
in: path
|
in: path
|
||||||
name: permissionId
|
name: uid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
|
||||||
example: 1234
|
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@@ -1749,14 +1793,11 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'The user''s identifier'
|
|
||||||
in: path
|
in: path
|
||||||
name: permissionId
|
name: uid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
format: double
|
type: string
|
||||||
type: number
|
|
||||||
example: 1234
|
|
||||||
/SASjsApi/session:
|
/SASjsApi/session:
|
||||||
get:
|
get:
|
||||||
operationId: Session
|
operationId: Session
|
||||||
@@ -1769,7 +1810,7 @@ paths:
|
|||||||
$ref: '#/components/schemas/SessionResponse'
|
$ref: '#/components/schemas/SessionResponse'
|
||||||
examples:
|
examples:
|
||||||
'Example 1':
|
'Example 1':
|
||||||
value: {id: 123, username: johnusername, displayName: John, isAdmin: false}
|
value: {id: userIdString, username: johnusername, displayName: John, isAdmin: false, needsToUpdatePassword: false}
|
||||||
summary: 'Get session info (username).'
|
summary: 'Get session info (username).'
|
||||||
tags:
|
tags:
|
||||||
- Session
|
- Session
|
||||||
@@ -1789,7 +1830,7 @@ paths:
|
|||||||
anyOf:
|
anyOf:
|
||||||
- {type: string}
|
- {type: string}
|
||||||
- {type: string, format: byte}
|
- {type: string, format: byte}
|
||||||
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts additional URL parameters (converted to session variables)\nand file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
|
||||||
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
|
||||||
tags:
|
tags:
|
||||||
- STP
|
- STP
|
||||||
@@ -1798,13 +1839,22 @@ paths:
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
description: 'Location of code in SASjs Drive'
|
description: 'Location of Stored Program in SASjs Drive.'
|
||||||
in: query
|
in: query
|
||||||
name: _program
|
name: _program
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: /Projects/myApp/some/program
|
example: /Projects/myApp/some/program
|
||||||
|
-
|
||||||
|
description: 'Optional query param for setting debug mode (returns the session log in the response body).'
|
||||||
|
in: query
|
||||||
|
name: _debug
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
format: double
|
||||||
|
type: number
|
||||||
|
example: 131
|
||||||
post:
|
post:
|
||||||
operationId: ExecutePostRequest
|
operationId: ExecutePostRequest
|
||||||
responses:
|
responses:
|
||||||
@@ -1863,7 +1913,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: {type: number, format: double}}, required: [needsToUpdatePassword, isAdmin, displayName, username, id], type: object}
|
user: {properties: {needsToUpdatePassword: {type: boolean}, isAdmin: {type: boolean}, displayName: {type: string}, username: {type: string}, id: {}}, 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: number, clientId: string, code: string) => {
|
static saveCode = (userId: string, 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: number, clientId: string) =>
|
static deleteCode = (userId: string, clientId: string) =>
|
||||||
delete AuthController.authCodes[userId][clientId]
|
delete AuthController.authCodes[userId][clientId]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +159,7 @@ const updatePassword = async (
|
|||||||
) => {
|
) => {
|
||||||
const { currentPassword, newPassword } = data
|
const { currentPassword, newPassword } = data
|
||||||
const userId = req.user?.userId
|
const userId = req.user?.userId
|
||||||
const dbUser = await User.findOne({ id: userId })
|
const dbUser = await User.findOne({ _id: userId })
|
||||||
|
|
||||||
if (!dbUser)
|
if (!dbUser)
|
||||||
throw {
|
throw {
|
||||||
|
|||||||
@@ -1,27 +1,55 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { Request, Security, Route, Tags, Post, Body } from 'tsoa'
|
import { Request, Security, Route, Tags, Post, Body } from 'tsoa'
|
||||||
import { ExecutionController } from './internal'
|
import { ExecutionController, getSessionController } from './internal'
|
||||||
import {
|
import {
|
||||||
getPreProgramVariables,
|
getPreProgramVariables,
|
||||||
getUserAutoExec,
|
getUserAutoExec,
|
||||||
ModeType,
|
ModeType,
|
||||||
parseLogToArray,
|
|
||||||
RunTimeType
|
RunTimeType
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
interface ExecuteCodePayload {
|
interface ExecuteCodePayload {
|
||||||
/**
|
/**
|
||||||
* Code of program
|
* The code to be executed
|
||||||
* @example "* Code HERE;"
|
* @example "* Your Code HERE;"
|
||||||
*/
|
*/
|
||||||
code: string
|
code: string
|
||||||
/**
|
/**
|
||||||
* runtime for program
|
* The runtime for the code - eg SAS, JS, PY or R
|
||||||
* @example "js"
|
* @example "js"
|
||||||
*/
|
*/
|
||||||
runTime: RunTimeType
|
runTime: RunTimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TriggerCodePayload {
|
||||||
|
/**
|
||||||
|
* The code to be executed
|
||||||
|
* @example "* Your Code HERE;"
|
||||||
|
*/
|
||||||
|
code: string
|
||||||
|
/**
|
||||||
|
* The runtime for the code - eg SAS, JS, PY or R
|
||||||
|
* @example "sas"
|
||||||
|
*/
|
||||||
|
runTime: RunTimeType
|
||||||
|
/**
|
||||||
|
* Amount of minutes after the completion of the job when the session must be
|
||||||
|
* destroyed.
|
||||||
|
* @example 15
|
||||||
|
*/
|
||||||
|
expiresAfterMins?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TriggerCodeResponse {
|
||||||
|
/**
|
||||||
|
* The SessionId is the name of the temporary folder used to store the outputs.
|
||||||
|
* For SAS, this would be the SASWORK folder. Can be used to poll job status.
|
||||||
|
* This session ID should be used to poll job status.
|
||||||
|
* @example "{ sessionId: '20241028074744-54132-1730101664824' }"
|
||||||
|
*/
|
||||||
|
sessionId: string
|
||||||
|
}
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/code')
|
@Route('SASjsApi/code')
|
||||||
@Tags('Code')
|
@Tags('Code')
|
||||||
@@ -44,6 +72,18 @@ export class CodeController {
|
|||||||
): Promise<string | Buffer> {
|
): Promise<string | Buffer> {
|
||||||
return executeCode(request, body)
|
return executeCode(request, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger Code on the Specified Runtime
|
||||||
|
* @summary Triggers code and returns SessionId immediately - does not wait for job completion
|
||||||
|
*/
|
||||||
|
@Post('/trigger')
|
||||||
|
public async triggerCode(
|
||||||
|
@Request() request: express.Request,
|
||||||
|
@Body() body: TriggerCodePayload
|
||||||
|
): Promise<TriggerCodeResponse> {
|
||||||
|
return triggerCode(request, body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeCode = async (
|
const executeCode = async (
|
||||||
@@ -76,3 +116,49 @@ const executeCode = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const triggerCode = async (
|
||||||
|
req: express.Request,
|
||||||
|
{ code, runTime, expiresAfterMins }: TriggerCodePayload
|
||||||
|
): Promise<{ sessionId: string }> => {
|
||||||
|
const { user } = req
|
||||||
|
const userAutoExec =
|
||||||
|
process.env.MODE === ModeType.Server
|
||||||
|
? user?.autoExec
|
||||||
|
: await getUserAutoExec()
|
||||||
|
|
||||||
|
// get session controller based on runTime
|
||||||
|
const sessionController = getSessionController(runTime)
|
||||||
|
|
||||||
|
// get session
|
||||||
|
const session = await sessionController.getSession()
|
||||||
|
|
||||||
|
// add expiresAfterMins to session if provided
|
||||||
|
if (expiresAfterMins) {
|
||||||
|
// expiresAfterMins.used is set initially to false
|
||||||
|
session.expiresAfterMins = { mins: expiresAfterMins, used: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// call executeProgram method of ExecutionController without awaiting
|
||||||
|
new ExecutionController().executeProgram({
|
||||||
|
program: code,
|
||||||
|
preProgramVariables: getPreProgramVariables(req),
|
||||||
|
vars: { ...req.query, _debug: 131 },
|
||||||
|
otherArgs: { userAutoExec },
|
||||||
|
runTime: runTime,
|
||||||
|
includePrintOutput: true,
|
||||||
|
session // session is provided
|
||||||
|
})
|
||||||
|
|
||||||
|
// return session id
|
||||||
|
return { sessionId: session.id }
|
||||||
|
} catch (err: any) {
|
||||||
|
throw {
|
||||||
|
code: 400,
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Job execution failed.',
|
||||||
|
error: typeof err === 'object' ? err.toString() : err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,28 +12,29 @@ 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 { AuthProviderType } from '../utils'
|
import { GetUserBy, UserResponse } from './user'
|
||||||
import { UserResponse } from './user'
|
|
||||||
|
|
||||||
export interface GroupResponse {
|
export interface GroupResponse {
|
||||||
groupId: number
|
uid: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupDetailsResponse {
|
export interface GroupDetailsResponse extends GroupResponse {
|
||||||
groupId: number
|
|
||||||
name: string
|
|
||||||
description: string
|
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
users: UserResponse[]
|
users: UserResponse[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetGroupBy {
|
interface GetGroupBy {
|
||||||
groupId?: number
|
_id?: string
|
||||||
name?: string
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum GroupAction {
|
||||||
|
AddUser = 'addUser',
|
||||||
|
RemoveUser = 'removeUser'
|
||||||
|
}
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@Route('SASjsApi/group')
|
@Route('SASjsApi/group')
|
||||||
@Tags('Group')
|
@Tags('Group')
|
||||||
@@ -44,7 +45,7 @@ export class GroupController {
|
|||||||
*/
|
*/
|
||||||
@Example<GroupResponse[]>([
|
@Example<GroupResponse[]>([
|
||||||
{
|
{
|
||||||
groupId: 123,
|
uid: 'groupIdString',
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users'
|
description: 'This group represents Data Controller Users'
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,7 @@ export class GroupController {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<GroupDetailsResponse>({
|
@Example<GroupDetailsResponse>({
|
||||||
groupId: 123,
|
uid: 'groupIdString',
|
||||||
name: 'DCGroup',
|
name: 'DCGroup',
|
||||||
description: 'This group represents Data Controller Users',
|
description: 'This group represents Data Controller Users',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
@@ -78,7 +79,7 @@ export class GroupController {
|
|||||||
* @example dcgroup
|
* @example dcgroup
|
||||||
*/
|
*/
|
||||||
@Get('by/groupname/{name}')
|
@Get('by/groupname/{name}')
|
||||||
public async getGroupByGroupName(
|
public async getGroupByName(
|
||||||
@Path() name: string
|
@Path() name: string
|
||||||
): Promise<GroupDetailsResponse> {
|
): Promise<GroupDetailsResponse> {
|
||||||
return getGroup({ name })
|
return getGroup({ name })
|
||||||
@@ -86,68 +87,66 @@ 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 groupId The group's identifier
|
* @param uid The group's identifier
|
||||||
* @example groupId 1234
|
* @example uid "12ByteString"
|
||||||
*/
|
*/
|
||||||
@Get('{groupId}')
|
@Get('{uid}')
|
||||||
public async getGroup(
|
public async getGroup(@Path() uid: string): Promise<GroupDetailsResponse> {
|
||||||
@Path() groupId: number
|
return getGroup({ _id: uid })
|
||||||
): 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 groupId The group's identifier
|
* @param groupUid The group's identifier
|
||||||
* @example groupId "1234"
|
* @example groupUid "12ByteString"
|
||||||
* @param userId The user's identifier
|
* @param userUid The user's identifier
|
||||||
* @example userId "6789"
|
* @example userId "12ByteString"
|
||||||
*/
|
*/
|
||||||
@Example<GroupDetailsResponse>({
|
@Example<GroupDetailsResponse>({
|
||||||
groupId: 123,
|
uid: 'groupIdString',
|
||||||
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('{groupId}/{userId}')
|
@Post('{groupUid}/{userUid}')
|
||||||
public async addUserToGroup(
|
public async addUserToGroup(
|
||||||
@Path() groupId: number,
|
@Path() groupUid: string,
|
||||||
@Path() userId: number
|
@Path() userUid: string
|
||||||
): Promise<GroupDetailsResponse> {
|
): Promise<GroupDetailsResponse> {
|
||||||
return addUserToGroup(groupId, userId)
|
return addUserToGroup(groupUid, userUid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Remove a user to a group. Admin task only.
|
* @summary Remove a user from a group. Admin task only.
|
||||||
* @param groupId The group's identifier
|
* @param groupUid The group's identifier
|
||||||
* @example groupId "1234"
|
* @example groupUid "12ByteString"
|
||||||
* @param userId The user's identifier
|
* @param userUid The user's identifier
|
||||||
* @example userId "6789"
|
* @example userUid "12ByteString"
|
||||||
*/
|
*/
|
||||||
@Example<GroupDetailsResponse>({
|
@Example<GroupDetailsResponse>({
|
||||||
groupId: 123,
|
uid: 'groupIdString',
|
||||||
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('{groupId}/{userId}')
|
@Delete('{groupUid}/{userUid}')
|
||||||
public async removeUserFromGroup(
|
public async removeUserFromGroup(
|
||||||
@Path() groupId: number,
|
@Path() groupUid: string,
|
||||||
@Path() userId: number
|
@Path() userUid: string
|
||||||
): Promise<GroupDetailsResponse> {
|
): Promise<GroupDetailsResponse> {
|
||||||
return removeUserFromGroup(groupId, userId)
|
return removeUserFromGroup(groupUid, userUid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Delete a group. Admin task only.
|
* @summary Delete a group. Admin task only.
|
||||||
* @param groupId The group's identifier
|
* @param uid The group's identifier
|
||||||
* @example groupId 1234
|
* @example uid "12ByteString"
|
||||||
*/
|
*/
|
||||||
@Delete('{groupId}')
|
@Delete('{uid}')
|
||||||
public async deleteGroup(@Path() groupId: number) {
|
public async deleteGroup(@Path() uid: string) {
|
||||||
const group = await Group.findOne({ groupId })
|
const group = await Group.findOne({ _id: uid })
|
||||||
if (!group)
|
if (!group)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -160,9 +159,7 @@ export class GroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAllGroups = async (): Promise<GroupResponse[]> =>
|
const getAllGroups = async (): Promise<GroupResponse[]> =>
|
||||||
await Group.find({})
|
await Group.find({}).select('uid name description').exec()
|
||||||
.select({ _id: 0, groupId: 1, name: 1, description: 1 })
|
|
||||||
.exec()
|
|
||||||
|
|
||||||
const createGroup = async ({
|
const createGroup = async ({
|
||||||
name,
|
name,
|
||||||
@@ -187,7 +184,7 @@ const createGroup = async ({
|
|||||||
const savedGroup = await group.save()
|
const savedGroup = await group.save()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groupId: savedGroup.groupId,
|
uid: savedGroup.uid,
|
||||||
name: savedGroup.name,
|
name: savedGroup.name,
|
||||||
description: savedGroup.description,
|
description: savedGroup.description,
|
||||||
isActive: savedGroup.isActive,
|
isActive: savedGroup.isActive,
|
||||||
@@ -198,11 +195,12 @@ 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,
|
||||||
'groupId name description isActive users -_id'
|
'uid name description isActive users'
|
||||||
).populate(
|
).populate(
|
||||||
'users',
|
'users',
|
||||||
'id username displayName isAdmin -_id'
|
'uid username displayName isAdmin'
|
||||||
)) as unknown as GroupDetailsResponse
|
)) as unknown as GroupDetailsResponse
|
||||||
|
|
||||||
if (!group)
|
if (!group)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -211,7 +209,7 @@ const getGroup = async (findBy: GetGroupBy): Promise<GroupDetailsResponse> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groupId: group.groupId,
|
uid: group.uid,
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description,
|
description: group.description,
|
||||||
isActive: group.isActive,
|
isActive: group.isActive,
|
||||||
@@ -220,23 +218,23 @@ const getGroup = async (findBy: GetGroupBy): Promise<GroupDetailsResponse> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addUserToGroup = async (
|
const addUserToGroup = async (
|
||||||
groupId: number,
|
groupUid: string,
|
||||||
userId: number
|
userUid: string
|
||||||
): Promise<GroupDetailsResponse> =>
|
): Promise<GroupDetailsResponse> =>
|
||||||
updateUsersListInGroup(groupId, userId, 'addUser')
|
updateUsersListInGroup(groupUid, userUid, GroupAction.AddUser)
|
||||||
|
|
||||||
const removeUserFromGroup = async (
|
const removeUserFromGroup = async (
|
||||||
groupId: number,
|
groupUid: string,
|
||||||
userId: number
|
userUid: string
|
||||||
): Promise<GroupDetailsResponse> =>
|
): Promise<GroupDetailsResponse> =>
|
||||||
updateUsersListInGroup(groupId, userId, 'removeUser')
|
updateUsersListInGroup(groupUid, userUid, GroupAction.RemoveUser)
|
||||||
|
|
||||||
const updateUsersListInGroup = async (
|
const updateUsersListInGroup = async (
|
||||||
groupId: number,
|
groupUid: string,
|
||||||
userId: number,
|
userUid: string,
|
||||||
action: 'addUser' | 'removeUser'
|
action: GroupAction
|
||||||
): Promise<GroupDetailsResponse> => {
|
): Promise<GroupDetailsResponse> => {
|
||||||
const group = await Group.findOne({ groupId })
|
const group = await Group.findOne({ _id: groupUid })
|
||||||
if (!group)
|
if (!group)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -258,7 +256,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: userId })
|
const user = await User.findOne({ _id: userUid })
|
||||||
if (!user)
|
if (!user)
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
@@ -274,7 +272,7 @@ const updateUsersListInGroup = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updatedGroup =
|
const updatedGroup =
|
||||||
action === 'addUser'
|
action === GroupAction.AddUser
|
||||||
? await group.addUser(user)
|
? await group.addUser(user)
|
||||||
: await group.removeUser(user)
|
: await group.removeUser(user)
|
||||||
|
|
||||||
@@ -286,7 +284,7 @@ const updateUsersListInGroup = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groupId: updatedGroup.groupId,
|
uid: updatedGroup.uid,
|
||||||
name: updatedGroup.name,
|
name: updatedGroup.name,
|
||||||
description: updatedGroup.description,
|
description: updatedGroup.description,
|
||||||
isActive: updatedGroup.isActive,
|
isActive: updatedGroup.isActive,
|
||||||
|
|||||||
@@ -128,8 +128,10 @@ export class ExecutionController {
|
|||||||
// INFO: webout can be a Buffer, that is why it's length should be checked to determine if it is empty
|
// INFO: webout can be a Buffer, that is why it's length should be checked to determine if it is empty
|
||||||
if (webout && webout.length !== 0) resultParts.push(webout)
|
if (webout && webout.length !== 0) resultParts.push(webout)
|
||||||
|
|
||||||
|
// INFO: log separator wraps the log from the beginning and the end
|
||||||
resultParts.push(process.logsUUID)
|
resultParts.push(process.logsUUID)
|
||||||
resultParts.push(log)
|
resultParts.push(log)
|
||||||
|
resultParts.push(process.logsUUID)
|
||||||
|
|
||||||
if (includePrintOutput && runTime === RunTimeType.SAS) {
|
if (includePrintOutput && runTime === RunTimeType.SAS) {
|
||||||
const printOutputPath = path.join(session.path, 'output.lst')
|
const printOutputPath = path.join(session.path, 'output.lst')
|
||||||
@@ -137,10 +139,7 @@ export class ExecutionController {
|
|||||||
? await readFile(printOutputPath)
|
? await readFile(printOutputPath)
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
if (printOutput) {
|
if (printOutput) resultParts.push(printOutput)
|
||||||
resultParts.push(process.logsUUID)
|
|
||||||
resultParts.push(printOutput)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import {
|
|||||||
createFile,
|
createFile,
|
||||||
fileExists,
|
fileExists,
|
||||||
generateTimestamp,
|
generateTimestamp,
|
||||||
readFile,
|
readFile
|
||||||
isWindows
|
|
||||||
} from '@sasjs/utils'
|
} from '@sasjs/utils'
|
||||||
|
|
||||||
const execFilePromise = promisify(execFile)
|
const execFilePromise = promisify(execFile)
|
||||||
@@ -190,17 +189,37 @@ ${autoExecContent}`
|
|||||||
}
|
}
|
||||||
|
|
||||||
private scheduleSessionDestroy(session: Session) {
|
private scheduleSessionDestroy(session: Session) {
|
||||||
setTimeout(async () => {
|
setTimeout(
|
||||||
if (session.inUse) {
|
async () => {
|
||||||
// adding 10 more minutes
|
if (session.inUse) {
|
||||||
const newDeathTimeStamp = parseInt(session.deathTimeStamp) + 10 * 1000
|
// adding 10 more minutes
|
||||||
session.deathTimeStamp = newDeathTimeStamp.toString()
|
const newDeathTimeStamp =
|
||||||
|
parseInt(session.deathTimeStamp) + 10 * 60 * 1000
|
||||||
|
session.deathTimeStamp = newDeathTimeStamp.toString()
|
||||||
|
|
||||||
this.scheduleSessionDestroy(session)
|
this.scheduleSessionDestroy(session)
|
||||||
} else {
|
} else {
|
||||||
await this.deleteSession(session)
|
const { expiresAfterMins } = session
|
||||||
}
|
|
||||||
}, parseInt(session.deathTimeStamp) - new Date().getTime() - 100)
|
// delay session destroy if expiresAfterMins present
|
||||||
|
if (expiresAfterMins && !expiresAfterMins.used) {
|
||||||
|
// calculate session death time using expiresAfterMins
|
||||||
|
const newDeathTimeStamp =
|
||||||
|
parseInt(session.deathTimeStamp) +
|
||||||
|
expiresAfterMins.mins * 60 * 1000
|
||||||
|
session.deathTimeStamp = newDeathTimeStamp.toString()
|
||||||
|
|
||||||
|
// set expiresAfterMins to true to avoid using it again
|
||||||
|
session.expiresAfterMins!.used = true
|
||||||
|
|
||||||
|
this.scheduleSessionDestroy(session)
|
||||||
|
} else {
|
||||||
|
await this.deleteSession(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseInt(session.deathTimeStamp) - new Date().getTime() - 100
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const createJSProgram = async (
|
|||||||
) => {
|
) => {
|
||||||
const varStatments = Object.keys(vars).reduce(
|
const varStatments = Object.keys(vars).reduce(
|
||||||
(computed: string, key: string) =>
|
(computed: string, key: string) =>
|
||||||
`${computed}const ${key} = '${vars[key]}';\n`,
|
`${computed}const ${key} = \`${vars[key]}\`;\n`,
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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 123
|
* @example 'groupIdString'
|
||||||
*/
|
*/
|
||||||
principalId: number
|
principalId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdatePermissionPayload {
|
interface UpdatePermissionPayload {
|
||||||
@@ -70,7 +70,7 @@ interface UpdatePermissionPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PermissionDetailsResponse {
|
export interface PermissionDetailsResponse {
|
||||||
permissionId: number
|
uid: string
|
||||||
path: string
|
path: string
|
||||||
type: string
|
type: string
|
||||||
setting: string
|
setting: string
|
||||||
@@ -91,24 +91,24 @@ export class PermissionController {
|
|||||||
*/
|
*/
|
||||||
@Example<PermissionDetailsResponse[]>([
|
@Example<PermissionDetailsResponse[]>([
|
||||||
{
|
{
|
||||||
permissionId: 123,
|
uid: 'permissionId1String',
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
user: {
|
user: {
|
||||||
id: 1,
|
uid: 'user1-id',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
permissionId: 124,
|
uid: 'permissionId2String',
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
group: {
|
group: {
|
||||||
groupId: 1,
|
uid: 'group1-id',
|
||||||
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>({
|
||||||
permissionId: 123,
|
uid: 'permissionIdString',
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
user: {
|
user: {
|
||||||
id: 1,
|
uid: 'userIdString',
|
||||||
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 1234
|
* @example permissionId "permissionIdString"
|
||||||
*/
|
*/
|
||||||
@Example<PermissionDetailsResponse>({
|
@Example<PermissionDetailsResponse>({
|
||||||
permissionId: 123,
|
uid: 'permissionIdString',
|
||||||
path: '/SASjsApi/code/execute',
|
path: '/SASjsApi/code/execute',
|
||||||
type: 'Route',
|
type: 'Route',
|
||||||
setting: 'Grant',
|
setting: 'Grant',
|
||||||
user: {
|
user: {
|
||||||
id: 1,
|
uid: 'userIdString',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@Patch('{permissionId}')
|
@Patch('{uid}')
|
||||||
public async updatePermission(
|
public async updatePermission(
|
||||||
@Path() permissionId: number,
|
@Path() uid: string,
|
||||||
@Body() body: UpdatePermissionPayload
|
@Body() body: UpdatePermissionPayload
|
||||||
): Promise<PermissionDetailsResponse> {
|
): Promise<PermissionDetailsResponse> {
|
||||||
return updatePermission(permissionId, body)
|
return updatePermission(uid, 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 1234
|
* @example permissionId "permissionIdString"
|
||||||
*/
|
*/
|
||||||
@Delete('{permissionId}')
|
@Delete('{uid}')
|
||||||
public async deletePermission(@Path() permissionId: number) {
|
public async deletePermission(@Path() uid: string) {
|
||||||
return deletePermission(permissionId)
|
return deletePermission(uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 = {
|
||||||
id: userInDB.id,
|
uid: userInDB.uid,
|
||||||
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({ groupId: principalId })
|
const groupInDB = await Group.findOne({ _id: 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 = {
|
||||||
groupId: groupInDB.groupId,
|
uid: groupInDB.uid,
|
||||||
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: 'id username displayName isAdmin -_id',
|
select: 'uid 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 {
|
||||||
permissionId: savedPermission.permissionId,
|
uid: savedPermission.uid,
|
||||||
path: savedPermission.path,
|
path: savedPermission.path,
|
||||||
type: savedPermission.type,
|
type: savedPermission.type,
|
||||||
setting: savedPermission.setting,
|
setting: savedPermission.setting,
|
||||||
@@ -324,27 +324,21 @@ const createPermission = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updatePermission = async (
|
const updatePermission = async (
|
||||||
id: number,
|
uid: string,
|
||||||
data: UpdatePermissionPayload
|
data: UpdatePermissionPayload
|
||||||
): Promise<PermissionDetailsResponse> => {
|
): Promise<PermissionDetailsResponse> => {
|
||||||
const { setting } = data
|
const { setting } = data
|
||||||
|
|
||||||
const updatedPermission = (await Permission.findOneAndUpdate(
|
const updatedPermission = (await Permission.findOneAndUpdate(
|
||||||
{ permissionId: id },
|
{ _id: uid },
|
||||||
{ setting },
|
{ setting },
|
||||||
{ new: true }
|
{ new: true }
|
||||||
)
|
)
|
||||||
.select({
|
.select('uid path type setting')
|
||||||
_id: 0,
|
.populate({ path: 'user', select: 'uid username displayName isAdmin' })
|
||||||
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 -_id'
|
select: 'groupId name description'
|
||||||
})) as unknown as PermissionDetailsResponse
|
})) as unknown as PermissionDetailsResponse
|
||||||
if (!updatedPermission)
|
if (!updatedPermission)
|
||||||
throw {
|
throw {
|
||||||
@@ -356,13 +350,13 @@ const updatePermission = async (
|
|||||||
return updatedPermission
|
return updatedPermission
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletePermission = async (id: number) => {
|
const deletePermission = async (uid: string) => {
|
||||||
const permission = await Permission.findOne({ permissionId: id })
|
const permission = await Permission.findOne({ _id: uid })
|
||||||
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({ permissionId: id })
|
await Permission.deleteOne({ _id: uid })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ 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'
|
||||||
|
|
||||||
interface SessionResponse extends UserResponse {
|
interface SessionResponse extends Omit<UserResponse, 'uid'> {
|
||||||
needsToUpdatePassword: boolean
|
id: string
|
||||||
|
needsToUpdatePassword?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@Security('bearerAuth')
|
@Security('bearerAuth')
|
||||||
@@ -14,11 +15,12 @@ export class SessionController {
|
|||||||
* @summary Get session info (username).
|
* @summary Get session info (username).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<UserResponse>({
|
@Example<SessionResponse>({
|
||||||
id: 123,
|
id: 'userIdString',
|
||||||
username: 'johnusername',
|
username: 'johnusername',
|
||||||
displayName: 'John',
|
displayName: 'John',
|
||||||
isAdmin: false
|
isAdmin: false,
|
||||||
|
needsToUpdatePassword: false
|
||||||
})
|
})
|
||||||
@Get('/')
|
@Get('/')
|
||||||
public async session(
|
public async session(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
getRunTimeAndFilePath
|
getRunTimeAndFilePath
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { MulterFile } from '../types/Upload'
|
import { MulterFile } from '../types/Upload'
|
||||||
|
import { debug } from 'console'
|
||||||
|
|
||||||
interface ExecutePostRequestPayload {
|
interface ExecutePostRequestPayload {
|
||||||
/**
|
/**
|
||||||
@@ -23,20 +24,30 @@ export class STPController {
|
|||||||
/**
|
/**
|
||||||
* Trigger a Stored Program using the _program URL parameter.
|
* Trigger a Stored Program using the _program URL parameter.
|
||||||
*
|
*
|
||||||
* Accepts URL parameters and file uploads. For more details, see docs:
|
* Accepts additional URL parameters (converted to session variables)
|
||||||
|
* and file uploads. For more details, see docs:
|
||||||
*
|
*
|
||||||
* https://server.sasjs.io/storedprograms
|
* https://server.sasjs.io/storedprograms
|
||||||
*
|
*
|
||||||
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
* @summary Execute a Stored Program, returns _webout and (optionally) log.
|
||||||
* @param _program Location of code in SASjs Drive
|
* @param _program Location of Stored Program in SASjs Drive.
|
||||||
|
* @param _debug Optional query param for setting debug mode (returns the session log in the response body).
|
||||||
* @example _program "/Projects/myApp/some/program"
|
* @example _program "/Projects/myApp/some/program"
|
||||||
|
* @example _debug 131
|
||||||
*/
|
*/
|
||||||
@Get('/execute')
|
@Get('/execute')
|
||||||
public async executeGetRequest(
|
public async executeGetRequest(
|
||||||
@Request() request: express.Request,
|
@Request() request: express.Request,
|
||||||
@Query() _program: string
|
@Query() _program: string,
|
||||||
|
@Query() _debug?: number
|
||||||
): Promise<string | Buffer> {
|
): Promise<string | Buffer> {
|
||||||
const vars = request.query as ExecutionVars
|
let vars = request.query as ExecutionVars
|
||||||
|
if (_debug) {
|
||||||
|
vars = {
|
||||||
|
...vars,
|
||||||
|
_debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return execute(request, _program, vars)
|
return execute(request, _program, vars)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,18 +26,14 @@ import {
|
|||||||
import { GroupController, GroupResponse } from './group'
|
import { GroupController, GroupResponse } from './group'
|
||||||
|
|
||||||
export interface UserResponse {
|
export interface UserResponse {
|
||||||
id: number
|
uid: string
|
||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserDetailsResponse {
|
export interface UserDetailsResponse extends UserResponse {
|
||||||
id: number
|
|
||||||
displayName: string
|
|
||||||
username: string
|
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
isAdmin: boolean
|
|
||||||
autoExec?: string
|
autoExec?: string
|
||||||
groups?: GroupResponse[]
|
groups?: GroupResponse[]
|
||||||
}
|
}
|
||||||
@@ -52,13 +48,13 @@ export class UserController {
|
|||||||
*/
|
*/
|
||||||
@Example<UserResponse[]>([
|
@Example<UserResponse[]>([
|
||||||
{
|
{
|
||||||
id: 123,
|
uid: 'userIdString',
|
||||||
username: 'johnusername',
|
username: 'johnusername',
|
||||||
displayName: 'John',
|
displayName: 'John',
|
||||||
isAdmin: false
|
isAdmin: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 456,
|
uid: 'anotherUserIdString',
|
||||||
username: 'starkusername',
|
username: 'starkusername',
|
||||||
displayName: 'Stark',
|
displayName: 'Stark',
|
||||||
isAdmin: true
|
isAdmin: true
|
||||||
@@ -74,7 +70,7 @@ export class UserController {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Example<UserDetailsResponse>({
|
@Example<UserDetailsResponse>({
|
||||||
id: 1234,
|
uid: 'userIdString',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
@@ -111,20 +107,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 1234
|
* @example userId "userIdString"
|
||||||
*/
|
*/
|
||||||
@Get('{userId}')
|
@Get('{uid}')
|
||||||
public async getUser(
|
public async getUser(
|
||||||
@Request() req: express.Request,
|
@Request() req: express.Request,
|
||||||
@Path() userId: number
|
@Path() uid: string
|
||||||
): 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 == userId
|
const getAutoExec = user!.isAdmin || user!.userId === uid
|
||||||
return getUser({ id: userId }, getAutoExec)
|
return getUser({ _id: uid }, getAutoExec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +129,7 @@ export class UserController {
|
|||||||
* @example username "johnSnow01"
|
* @example username "johnSnow01"
|
||||||
*/
|
*/
|
||||||
@Example<UserDetailsResponse>({
|
@Example<UserDetailsResponse>({
|
||||||
id: 1234,
|
uid: 'userIdString',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
@@ -158,7 +154,7 @@ export class UserController {
|
|||||||
* @example userId "1234"
|
* @example userId "1234"
|
||||||
*/
|
*/
|
||||||
@Example<UserDetailsResponse>({
|
@Example<UserDetailsResponse>({
|
||||||
id: 1234,
|
uid: 'userIdString',
|
||||||
displayName: 'John Snow',
|
displayName: 'John Snow',
|
||||||
username: 'johnSnow01',
|
username: 'johnSnow01',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
@@ -166,7 +162,7 @@ export class UserController {
|
|||||||
})
|
})
|
||||||
@Patch('{userId}')
|
@Patch('{userId}')
|
||||||
public async updateUser(
|
public async updateUser(
|
||||||
@Path() userId: number,
|
@Path() userId: string,
|
||||||
@Body() body: UserPayload
|
@Body() body: UserPayload
|
||||||
): Promise<UserDetailsResponse> {
|
): Promise<UserDetailsResponse> {
|
||||||
const { MODE } = process.env
|
const { MODE } = process.env
|
||||||
@@ -174,7 +170,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -198,18 +194,16 @@ export class UserController {
|
|||||||
*/
|
*/
|
||||||
@Delete('{userId}')
|
@Delete('{userId}')
|
||||||
public async deleteUser(
|
public async deleteUser(
|
||||||
@Path() userId: number,
|
@Path() userId: string,
|
||||||
@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({})
|
await User.find({}).select('uid username displayName isAdmin').exec()
|
||||||
.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
|
||||||
@@ -239,15 +233,15 @@ const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
|||||||
|
|
||||||
const groupController = new GroupController()
|
const groupController = new GroupController()
|
||||||
const allUsersGroup = await groupController
|
const allUsersGroup = await groupController
|
||||||
.getGroupByGroupName(ALL_USERS_GROUP.name)
|
.getGroupByName(ALL_USERS_GROUP.name)
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
|
|
||||||
if (allUsersGroup) {
|
if (allUsersGroup) {
|
||||||
await groupController.addUserToGroup(allUsersGroup.groupId, savedUser.id)
|
await groupController.addUserToGroup(allUsersGroup.uid, savedUser.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: savedUser.id,
|
uid: savedUser.uid,
|
||||||
displayName: savedUser.displayName,
|
displayName: savedUser.displayName,
|
||||||
username: savedUser.username,
|
username: savedUser.username,
|
||||||
isActive: savedUser.isActive,
|
isActive: savedUser.isActive,
|
||||||
@@ -256,8 +250,8 @@ const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetUserBy {
|
export interface GetUserBy {
|
||||||
id?: number
|
_id?: string
|
||||||
username?: string
|
username?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,10 +261,10 @@ const getUser = async (
|
|||||||
): Promise<UserDetailsResponse> => {
|
): Promise<UserDetailsResponse> => {
|
||||||
const user = (await User.findOne(
|
const user = (await User.findOne(
|
||||||
findBy,
|
findBy,
|
||||||
`id displayName username isActive isAdmin autoExec -_id`
|
`uid displayName username isActive isAdmin autoExec`
|
||||||
).populate(
|
).populate(
|
||||||
'groups',
|
'groups',
|
||||||
'groupId name description -_id'
|
'uid name description'
|
||||||
)) as unknown as UserDetailsResponse
|
)) as unknown as UserDetailsResponse
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
@@ -280,12 +274,12 @@ const getUser = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
uid: user.uid,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
isActive: user.isActive,
|
isActive: user.isActive,
|
||||||
isAdmin: user.isAdmin,
|
isAdmin: user.isAdmin,
|
||||||
autoExec: getAutoExec ? user.autoExec ?? '' : undefined,
|
autoExec: getAutoExec ? (user.autoExec ?? '') : undefined,
|
||||||
groups: user.groups
|
groups: user.groups
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +287,7 @@ const getUser = async (
|
|||||||
const getDesktopAutoExec = async () => {
|
const getDesktopAutoExec = async () => {
|
||||||
return {
|
return {
|
||||||
...desktopUser,
|
...desktopUser,
|
||||||
id: desktopUser.userId,
|
uid: desktopUser.userId,
|
||||||
autoExec: await getUserAutoExec()
|
autoExec: await getUserAutoExec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,8 +323,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.id != findBy.id) ||
|
(findBy._id && usernameExist.uid !== findBy._id) ||
|
||||||
(findBy.username && usernameExist.username != findBy.username)
|
(findBy.username && usernameExist.username !== findBy.username)
|
||||||
)
|
)
|
||||||
throw {
|
throw {
|
||||||
code: 409,
|
code: 409,
|
||||||
@@ -350,11 +344,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 {
|
||||||
id: updatedUser.id,
|
uid: updatedUser.uid,
|
||||||
username: updatedUser.username,
|
username: updatedUser.username,
|
||||||
displayName: updatedUser.displayName,
|
displayName: updatedUser.displayName,
|
||||||
isAdmin: updatedUser.isAdmin,
|
isAdmin: updatedUser.isAdmin,
|
||||||
@@ -367,7 +361,7 @@ const updateDesktopAutoExec = async (autoExec: string) => {
|
|||||||
await updateUserAutoExec(autoExec)
|
await updateUserAutoExec(autoExec)
|
||||||
return {
|
return {
|
||||||
...desktopUser,
|
...desktopUser,
|
||||||
id: desktopUser.userId,
|
uid: desktopUser.userId,
|
||||||
autoExec
|
autoExec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const authenticateToken = async (
|
|||||||
const { MODE } = process.env
|
const { MODE } = process.env
|
||||||
if (MODE === ModeType.Desktop) {
|
if (MODE === ModeType.Desktop) {
|
||||||
req.user = {
|
req.user = {
|
||||||
userId: 1234,
|
userId: '1234',
|
||||||
clientId: 'desktopModeClientId',
|
clientId: 'desktopModeClientId',
|
||||||
username: 'desktopModeUsername',
|
username: 'desktopModeUsername',
|
||||||
displayName: 'desktopModeDisplayName',
|
displayName: 'desktopModeDisplayName',
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const authorize: RequestHandler = async (req, res, next) => {
|
|||||||
// no need to check for permissions when route is Public
|
// no need to check for permissions when route is Public
|
||||||
if (await isPublicRoute(req)) return next()
|
if (await isPublicRoute(req)) return next()
|
||||||
|
|
||||||
const dbUser = await User.findOne({ id: user.userId })
|
const dbUser = await User.findOne({ _id: user.userId })
|
||||||
if (!dbUser) return res.sendStatus(401)
|
if (!dbUser) return res.sendStatus(401)
|
||||||
|
|
||||||
const path = getPath(req)
|
const path = getPath(req)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const desktopRestrict: RequestHandler = (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const desktopUser: RequestUser = {
|
export const desktopUser: RequestUser = {
|
||||||
userId: 12345,
|
userId: '12345',
|
||||||
clientId: 'desktop_app',
|
clientId: 'desktop_app',
|
||||||
username: userInfo().username,
|
username: userInfo().username,
|
||||||
displayName: userInfo().username,
|
displayName: userInfo().username,
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export const verifyAdminIfNeeded: RequestHandler = (req, res, next) => {
|
|||||||
if (!user?.isAdmin) {
|
if (!user?.isAdmin) {
|
||||||
let adminAccountRequired: boolean = true
|
let adminAccountRequired: boolean = true
|
||||||
|
|
||||||
if (req.params.userId) {
|
if (req.params.uid) {
|
||||||
adminAccountRequired = user?.userId !== parseInt(req.params.userId)
|
adminAccountRequired = user?.userId !== req.params.uid
|
||||||
} else if (req.params.username) {
|
} else if (req.params.username) {
|
||||||
adminAccountRequired = user?.username !== req.params.username
|
adminAccountRequired = user?.username !== req.params.username
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
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, getSequenceNextValue } from '../utils'
|
import { AuthProviderType } from '../utils'
|
||||||
|
|
||||||
export const PUBLIC_GROUP_NAME = 'Public'
|
export const PUBLIC_GROUP_NAME = 'public'
|
||||||
|
|
||||||
export interface GroupPayload {
|
export interface GroupPayload {
|
||||||
/**
|
/**
|
||||||
@@ -24,10 +24,12 @@ 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 {
|
||||||
@@ -37,40 +39,46 @@ interface IGroup extends IGroupDocument {
|
|||||||
}
|
}
|
||||||
interface IGroupModel extends Model<IGroup> {}
|
interface IGroupModel extends Model<IGroup> {}
|
||||||
|
|
||||||
const groupSchema = new Schema<IGroupDocument>({
|
const opts = {
|
||||||
name: {
|
toJSON: {
|
||||||
type: String,
|
virtuals: true,
|
||||||
required: true,
|
transform: function (doc: any, ret: any, options: any) {
|
||||||
unique: true
|
delete ret._id
|
||||||
},
|
delete ret.id
|
||||||
groupId: {
|
return ret
|
||||||
type: Number,
|
}
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: 'Group description.'
|
|
||||||
},
|
|
||||||
authProvider: {
|
|
||||||
type: String,
|
|
||||||
enum: AuthProviderType
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
groupSchema.pre('save', async function () {
|
|
||||||
if (this.isNew) {
|
|
||||||
this.groupId = await getSequenceNextValue('groupId')
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const groupSchema = new Schema<IGroupDocument>(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: 'Group description.'
|
||||||
|
},
|
||||||
|
authProvider: {
|
||||||
|
type: String,
|
||||||
|
enum: AuthProviderType
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
|
||||||
|
},
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
groupSchema.virtual('uid').get(function () {
|
||||||
|
return this._id.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
groupSchema.post('save', function (group: IGroup, next: Function) {
|
groupSchema.post('save', function (group: IGroup, next: Function) {
|
||||||
group.populate('users', 'id username displayName -_id').then(function () {
|
group.populate('users', 'uid username displayName').then(function () {
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
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
|
||||||
@@ -11,9 +10,11 @@ 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 {}
|
||||||
@@ -22,32 +23,39 @@ interface IPermissionModel extends Model<IPermission> {
|
|||||||
get(getBy: GetPermissionBy): Promise<PermissionDetailsResponse[]>
|
get(getBy: GetPermissionBy): Promise<PermissionDetailsResponse[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
const permissionSchema = new Schema<IPermissionDocument>({
|
const opts = {
|
||||||
permissionId: {
|
toJSON: {
|
||||||
type: Number,
|
virtuals: true,
|
||||||
unique: true
|
transform: function (doc: any, ret: any, options: any) {
|
||||||
},
|
delete ret._id
|
||||||
path: {
|
delete ret.id
|
||||||
type: String,
|
return ret
|
||||||
required: true
|
}
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
setting: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
user: { type: Schema.Types.ObjectId, ref: 'User' },
|
|
||||||
group: { type: Schema.Types.ObjectId, ref: 'Group' }
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
permissionSchema.pre('save', async function () {
|
|
||||||
if (this.isNew) {
|
|
||||||
this.permissionId = await getSequenceNextValue('permissionId')
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionSchema = new Schema<IPermissionDocument>(
|
||||||
|
{
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
user: { type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
|
group: { type: Schema.Types.ObjectId, ref: 'Group' }
|
||||||
|
},
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
permissionSchema.virtual('uid').get(function () {
|
||||||
|
return this._id.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Static Methods
|
// Static Methods
|
||||||
@@ -55,20 +63,14 @@ permissionSchema.static('get', async function (getBy: GetPermissionBy): Promise<
|
|||||||
PermissionDetailsResponse[]
|
PermissionDetailsResponse[]
|
||||||
> {
|
> {
|
||||||
return (await this.find(getBy)
|
return (await this.find(getBy)
|
||||||
.select({
|
.select('uid path type setting')
|
||||||
_id: 0,
|
.populate({ path: 'user', select: 'uid username displayName isAdmin' })
|
||||||
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 -_id',
|
select: 'uid name description',
|
||||||
populate: {
|
populate: {
|
||||||
path: 'users',
|
path: 'users',
|
||||||
select: 'id username displayName isAdmin -_id',
|
select: 'uid username displayName isAdmin',
|
||||||
options: { limit: 15 }
|
options: { limit: 15 }
|
||||||
}
|
}
|
||||||
})) as unknown as PermissionDetailsResponse[]
|
})) as unknown as PermissionDetailsResponse[]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Schema, model, Document, Model } from 'mongoose'
|
import { Schema, model, Document, Model, ObjectId } from 'mongoose'
|
||||||
import bcrypt from 'bcryptjs'
|
import bcrypt from 'bcryptjs'
|
||||||
import { AuthProviderType, getSequenceNextValue } from '../utils'
|
import { AuthProviderType } from '../utils'
|
||||||
|
|
||||||
export interface UserPayload {
|
export interface UserPayload {
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +36,6 @@ 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
|
||||||
@@ -44,6 +43,9 @@ 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 {
|
||||||
@@ -54,70 +56,74 @@ 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 = {
|
||||||
const userSchema = new Schema<IUserDocument>({
|
toJSON: {
|
||||||
displayName: {
|
virtuals: true,
|
||||||
type: String,
|
transform: function (doc: any, ret: any, options: any) {
|
||||||
required: true
|
delete ret._id
|
||||||
},
|
delete ret.id
|
||||||
username: {
|
return ret
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
type: Number,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
authProvider: {
|
|
||||||
type: String,
|
|
||||||
enum: AuthProviderType
|
|
||||||
},
|
|
||||||
isAdmin: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
needsToUpdatePassword: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
autoExec: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
|
|
||||||
tokens: [
|
|
||||||
{
|
|
||||||
clientId: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
accessToken: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
refreshToken: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
userSchema.pre('save', async function (next) {
|
|
||||||
if (this.isNew) {
|
|
||||||
this.id = await getSequenceNextValue('id')
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
next()
|
const userSchema = new Schema<IUserDocument>(
|
||||||
|
{
|
||||||
|
displayName: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
authProvider: {
|
||||||
|
type: String,
|
||||||
|
enum: AuthProviderType
|
||||||
|
},
|
||||||
|
isAdmin: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
needsToUpdatePassword: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
autoExec: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
|
||||||
|
tokens: [
|
||||||
|
{
|
||||||
|
clientId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
accessToken: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
refreshToken: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
userSchema.virtual('uid').get(function () {
|
||||||
|
return this._id.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Static Methods
|
// Static Methods
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { runCodeValidation } from '../../utils'
|
import { runCodeValidation, triggerCodeValidation } from '../../utils'
|
||||||
import { CodeController } from '../../controllers/'
|
import { CodeController } from '../../controllers/'
|
||||||
|
|
||||||
const runRouter = express.Router()
|
const runRouter = express.Router()
|
||||||
@@ -28,4 +28,22 @@ runRouter.post('/execute', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
runRouter.post('/trigger', async (req, res) => {
|
||||||
|
const { error, value: body } = triggerCodeValidation(req.body)
|
||||||
|
if (error) return res.status(400).send(error.details[0].message)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await controller.triggerCode(req, body)
|
||||||
|
|
||||||
|
res.status(200)
|
||||||
|
res.send(response)
|
||||||
|
} catch (err: any) {
|
||||||
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export default runRouter
|
export default runRouter
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
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 { getGroupValidation, registerGroupValidation } from '../../utils'
|
import {
|
||||||
|
getGroupValidation,
|
||||||
|
registerGroupValidation,
|
||||||
|
uidValidation
|
||||||
|
} from '../../utils'
|
||||||
|
|
||||||
const groupRouter = express.Router()
|
const groupRouter = express.Router()
|
||||||
|
|
||||||
@@ -33,12 +37,15 @@ groupRouter.get('/', authenticateAccessToken, async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
groupRouter.get('/:groupId', authenticateAccessToken, async (req, res) => {
|
groupRouter.get('/:uid', authenticateAccessToken, async (req, res) => {
|
||||||
const { groupId } = req.params
|
const { error: uidError, value: params } = uidValidation(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(parseInt(groupId))
|
const response = await controller.getGroup(uid)
|
||||||
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)
|
||||||
@@ -56,7 +63,7 @@ groupRouter.get(
|
|||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.getGroupByGroupName(name)
|
const response = await controller.getGroupByName(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)
|
||||||
@@ -65,18 +72,15 @@ groupRouter.get(
|
|||||||
)
|
)
|
||||||
|
|
||||||
groupRouter.post(
|
groupRouter.post(
|
||||||
'/:groupId/:userId',
|
'/:groupUid/:userUid',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdmin,
|
verifyAdmin,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { groupId, userId } = req.params
|
const { groupUid, userUid } = req.params
|
||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.addUserToGroup(
|
const response = await controller.addUserToGroup(groupUid, userUid)
|
||||||
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)
|
||||||
@@ -85,18 +89,15 @@ groupRouter.post(
|
|||||||
)
|
)
|
||||||
|
|
||||||
groupRouter.delete(
|
groupRouter.delete(
|
||||||
'/:groupId/:userId',
|
'/:groupUid/:userUid',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdmin,
|
verifyAdmin,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { groupId, userId } = req.params
|
const { groupUid, userUid } = req.params
|
||||||
|
|
||||||
const controller = new GroupController()
|
const controller = new GroupController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.removeUserFromGroup(
|
const response = await controller.removeUserFromGroup(groupUid, userUid)
|
||||||
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)
|
||||||
@@ -105,15 +106,18 @@ groupRouter.delete(
|
|||||||
)
|
)
|
||||||
|
|
||||||
groupRouter.delete(
|
groupRouter.delete(
|
||||||
'/:groupId',
|
'/:uid',
|
||||||
authenticateAccessToken,
|
authenticateAccessToken,
|
||||||
verifyAdmin,
|
verifyAdmin,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { groupId } = req.params
|
const { error: uidError, value: params } = uidValidation(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(parseInt(groupId))
|
await controller.deleteGroup(uid)
|
||||||
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,6 +3,7 @@ import { PermissionController } from '../../controllers/'
|
|||||||
import { verifyAdmin } from '../../middlewares'
|
import { verifyAdmin } from '../../middlewares'
|
||||||
import {
|
import {
|
||||||
registerPermissionValidation,
|
registerPermissionValidation,
|
||||||
|
uidValidation,
|
||||||
updatePermissionValidation
|
updatePermissionValidation
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
@@ -34,14 +35,17 @@ permissionRouter.post('/', verifyAdmin, async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionRouter.patch('/:permissionId', verifyAdmin, async (req: any, res) => {
|
permissionRouter.patch('/:uid', verifyAdmin, async (req: any, res) => {
|
||||||
const { permissionId } = req.params
|
const { error: uidError, value: params } = uidValidation(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(permissionId, body)
|
const response = await controller.updatePermission(uid, body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const statusCode = err.code
|
const statusCode = err.code
|
||||||
@@ -50,20 +54,18 @@ permissionRouter.patch('/:permissionId', verifyAdmin, async (req: any, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionRouter.delete(
|
permissionRouter.delete('/:uid', verifyAdmin, async (req: any, res) => {
|
||||||
'/:permissionId',
|
const { error: uidError, value: params } = uidValidation(req.params)
|
||||||
verifyAdmin,
|
if (uidError) return res.status(400).send(uidError.details[0].message)
|
||||||
async (req: any, res) => {
|
|
||||||
const { permissionId } = req.params
|
|
||||||
|
|
||||||
try {
|
const { uid } = params
|
||||||
await controller.deletePermission(permissionId)
|
try {
|
||||||
res.status(200).send('Permission Deleted!')
|
await controller.deletePermission(uid)
|
||||||
} catch (err: any) {
|
res.status(200).send('Permission Deleted!')
|
||||||
const statusCode = err.code
|
} catch (err: any) {
|
||||||
delete err.code
|
const statusCode = err.code
|
||||||
res.status(statusCode).send(err.message)
|
delete err.code
|
||||||
}
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
export default permissionRouter
|
export default permissionRouter
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
generateAccessToken,
|
generateAccessToken,
|
||||||
generateAuthCode,
|
generateAuthCode,
|
||||||
generateRefreshToken,
|
generateRefreshToken,
|
||||||
|
randomBytesHexString,
|
||||||
saveTokensInDB,
|
saveTokensInDB,
|
||||||
verifyTokenInDB
|
verifyTokenInDB
|
||||||
} from '../../../utils'
|
} from '../../../utils'
|
||||||
@@ -20,7 +21,6 @@ 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: user.id
|
userId: randomBytesHexString(12)
|
||||||
}
|
}
|
||||||
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.id
|
userId: currentUser.uid
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
currentUser.id,
|
currentUser.uid,
|
||||||
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.id
|
userId: currentUser.uid
|
||||||
})
|
})
|
||||||
|
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
currentUser.id,
|
currentUser.uid,
|
||||||
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.id
|
userId: dbUser.uid
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
dbUser.id,
|
dbUser.uid,
|
||||||
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.id
|
userId: dbUser.uid
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
dbUser.id,
|
dbUser.uid,
|
||||||
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.id
|
userId: dbUser.uid
|
||||||
})
|
})
|
||||||
await saveTokensInDB(
|
await saveTokensInDB(
|
||||||
dbUser.id,
|
dbUser.uid,
|
||||||
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.id)
|
accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/deploy',
|
path: '/SASjsApi/drive/deploy',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/deploy/upload',
|
path: '/SASjsApi/drive/deploy/upload',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/file',
|
path: '/SASjsApi/drive/file',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/folder',
|
path: '/SASjsApi/drive/folder',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/rename',
|
path: '/SASjsApi/drive/rename',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -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: number) => {
|
const generateAndSaveToken = async (userId: string) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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 = {
|
||||||
@@ -75,7 +76,7 @@ describe('group', () => {
|
|||||||
.send(group)
|
.send(group)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -155,7 +156,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.groupId}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -174,17 +175,17 @@ describe('group', () => {
|
|||||||
username: 'deletegroup2'
|
username: 'deletegroup2'
|
||||||
})
|
})
|
||||||
|
|
||||||
await groupController.addUserToGroup(dbGroup.groupId, dbUser1.id)
|
await groupController.addUserToGroup(dbGroup.uid, dbUser1.uid)
|
||||||
await groupController.addUserToGroup(dbGroup.groupId, dbUser2.id)
|
await groupController.addUserToGroup(dbGroup.uid, dbUser2.uid)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}`)
|
||||||
.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.id}`)
|
.get(`/SASjsApi/user/${dbUser1.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -192,7 +193,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.id}`)
|
.get(`/SASjsApi/user/${dbUser2.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -201,8 +202,10 @@ 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/1234`)
|
.delete(`/SASjsApi/group/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -229,7 +232,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -245,15 +248,15 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with group', async () => {
|
it('should respond with group', async () => {
|
||||||
const { groupId } = await groupController.createGroup(group)
|
const { uid } = await groupController.createGroup(group)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/group/${groupId}`)
|
.get(`/SASjsApi/group/${uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -266,15 +269,15 @@ describe('group', () => {
|
|||||||
username: 'get' + user.username
|
username: 'get' + user.username
|
||||||
})
|
})
|
||||||
|
|
||||||
const { groupId } = await groupController.createGroup(group)
|
const { uid } = await groupController.createGroup(group)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/group/${groupId}`)
|
.get(`/SASjsApi/group/${uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -292,8 +295,10 @@ 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/1234')
|
.get(`/SASjsApi/group/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -312,7 +317,7 @@ describe('group', () => {
|
|||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -333,7 +338,7 @@ describe('group', () => {
|
|||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -379,7 +384,7 @@ describe('group', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
groupId: expect.anything(),
|
uid: expect.anything(),
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description
|
description: group.description
|
||||||
}
|
}
|
||||||
@@ -401,7 +406,7 @@ describe('group', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
groupId: expect.anything(),
|
uid: expect.anything(),
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description
|
description: group.description
|
||||||
}
|
}
|
||||||
@@ -426,18 +431,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.groupId}/${dbUser.id}`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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([
|
||||||
{
|
{
|
||||||
id: expect.anything(),
|
uid: expect.anything(),
|
||||||
username: user.username,
|
username: user.username,
|
||||||
displayName: user.displayName
|
displayName: user.displayName
|
||||||
}
|
}
|
||||||
@@ -452,20 +457,20 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.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.id}`)
|
.get(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groups).toEqual([
|
expect(res.body.groups).toEqual([
|
||||||
{
|
{
|
||||||
groupId: expect.anything(),
|
uid: expect.anything(),
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description
|
description: group.description
|
||||||
}
|
}
|
||||||
@@ -478,21 +483,21 @@ describe('group', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'addUserRandomUser'
|
username: 'addUserRandomUser'
|
||||||
})
|
})
|
||||||
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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([
|
||||||
{
|
{
|
||||||
id: expect.anything(),
|
uid: expect.anything(),
|
||||||
username: 'addUserRandomUser',
|
username: 'addUserRandomUser',
|
||||||
displayName: user.displayName
|
displayName: user.displayName
|
||||||
}
|
}
|
||||||
@@ -526,8 +531,10 @@ 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/123/123')
|
.post(`/SASjsApi/group/${hexValue}/123`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -538,8 +545,10 @@ 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.groupId}/123`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -556,7 +565,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -577,7 +586,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -596,7 +605,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -618,15 +627,15 @@ describe('group', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'removeUserRandomUser'
|
username: 'removeUserRandomUser'
|
||||||
})
|
})
|
||||||
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.groupId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -639,16 +648,16 @@ describe('group', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'removeGroupFromUser'
|
username: 'removeGroupFromUser'
|
||||||
})
|
})
|
||||||
await groupController.addUserToGroup(dbGroup.groupId, dbUser.id)
|
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.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.id}`)
|
.get(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -667,7 +676,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -686,7 +695,7 @@ describe('group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(405)
|
.expect(405)
|
||||||
@@ -723,8 +732,10 @@ 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/123/123')
|
.delete(`/SASjsApi/group/${hexValue}/123`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -735,8 +746,10 @@ 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.groupId}/123`)
|
.delete(`/SASjsApi/group/${dbGroup.uid}/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -752,10 +765,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.id)
|
return generateAndSaveToken(dbUser.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: number) => {
|
const generateAndSaveToken = async (userId: string) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ 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',
|
||||||
@@ -103,10 +104,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.id })
|
.send({ ...permission, principalId: dbUser.uid })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.permissionId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -122,11 +123,11 @@ describe('permission', () => {
|
|||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalType: 'group',
|
principalType: 'group',
|
||||||
principalId: dbGroup.groupId
|
principalId: dbGroup.uid
|
||||||
})
|
})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.permissionId).toBeTruthy()
|
expect(res.body.uid).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)
|
||||||
@@ -144,7 +145,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/permission')
|
.post('/SASjsApi/permission')
|
||||||
@@ -281,17 +282,19 @@ describe('permission', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Bad Request if principalId is not a number', async () => {
|
it('should respond with Bad Request if principalId is not a string of 24 hex characters', 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: 'someCharacters'
|
principalId: randomBytes(10).toString('hex')
|
||||||
})
|
})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
|
|
||||||
expect(res.text).toEqual('"principalId" must be a number')
|
expect(res.text).toEqual(
|
||||||
|
'"principalId" length must be 24 characters long'
|
||||||
|
)
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -307,7 +310,7 @@ describe('permission', () => {
|
|||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: adminUser.id
|
principalId: adminUser.uid
|
||||||
})
|
})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
|
|
||||||
@@ -321,7 +324,7 @@ describe('permission', () => {
|
|||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: 123
|
principalId: randomBytes(12).toString('hex')
|
||||||
})
|
})
|
||||||
.expect(404)
|
.expect(404)
|
||||||
|
|
||||||
@@ -336,7 +339,7 @@ describe('permission', () => {
|
|||||||
.send({
|
.send({
|
||||||
...permission,
|
...permission,
|
||||||
principalType: 'group',
|
principalType: 'group',
|
||||||
principalId: 123
|
principalId: randomBytes(12).toString('hex')
|
||||||
})
|
})
|
||||||
.expect(404)
|
.expect(404)
|
||||||
|
|
||||||
@@ -347,13 +350,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.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
|
|
||||||
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.id })
|
.send({ ...permission, principalId: dbUser.uid })
|
||||||
.expect(409)
|
.expect(409)
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
expect(res.text).toEqual(
|
||||||
@@ -368,7 +371,7 @@ describe('permission', () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
dbPermission = await permissionController.createPermission({
|
dbPermission = await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -378,7 +381,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?.permissionId}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ setting: PermissionSettingForRoute.deny })
|
.send({ setting: PermissionSettingForRoute.deny })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -388,7 +391,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?.permissionId}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
||||||
.send()
|
.send()
|
||||||
.expect(401)
|
.expect(401)
|
||||||
|
|
||||||
@@ -403,7 +406,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -414,7 +417,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?.permissionId}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -425,7 +428,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?.permissionId}`)
|
.patch(`/SASjsApi/permission/${dbPermission?.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
setting: 'invalid'
|
setting: 'invalid'
|
||||||
@@ -437,8 +440,9 @@ 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/123')
|
.patch(`/SASjsApi/permission/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
setting: PermissionSettingForRoute.deny
|
setting: PermissionSettingForRoute.deny
|
||||||
@@ -454,10 +458,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.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/permission/${dbPermission?.permissionId}`)
|
.delete(`/SASjsApi/permission/${dbPermission?.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -466,8 +470,10 @@ 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/123')
|
.delete(`/SASjsApi/permission/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -481,12 +487,12 @@ describe('permission', () => {
|
|||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/test-1',
|
path: '/test-1',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/test-2',
|
path: '/test-2',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -505,12 +511,12 @@ describe('permission', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'get' + user.username
|
username: 'get' + user.username
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(nonAdminUser.id)
|
const accessToken = await generateAndSaveToken(nonAdminUser.uid)
|
||||||
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.id,
|
principalId: nonAdminUser.uid,
|
||||||
setting: PermissionSettingForRoute.grant
|
setting: PermissionSettingForRoute.grant
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -531,7 +537,7 @@ describe('permission', () => {
|
|||||||
await permissionController.createPermission({
|
await permissionController.createPermission({
|
||||||
...permission,
|
...permission,
|
||||||
path: '/SASjsApi/drive/deploy',
|
path: '/SASjsApi/drive/deploy',
|
||||||
principalId: dbUser.id
|
principalId: dbUser.uid
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -551,7 +557,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create files in SASJS drive', async () => {
|
it('should create files in SASJS drive', async () => {
|
||||||
const accessToken = await generateAndSaveToken(dbUser.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get('/SASjsApi/drive/deploy')
|
.get('/SASjsApi/drive/deploy')
|
||||||
@@ -561,7 +567,7 @@ describe('permission', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should respond unauthorized', async () => {
|
it('should respond unauthorized', async () => {
|
||||||
const accessToken = await generateAndSaveToken(dbUser.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get('/SASjsApi/drive/deploy/upload')
|
.get('/SASjsApi/drive/deploy/upload')
|
||||||
@@ -577,10 +583,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.id)
|
return generateAndSaveToken(dbUser.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: number) => {
|
const generateAndSaveToken = async (userId: string) => {
|
||||||
const adminAccessToken = generateAccessToken({
|
const adminAccessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -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.id)
|
accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
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.id,
|
principalId: dbUser.uid,
|
||||||
setting: PermissionSettingForRoute.grant
|
setting: PermissionSettingForRoute.grant
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -456,7 +456,7 @@ const makeRequestAndAssert = async (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: number) => {
|
const generateAndSaveToken = async (userId: string) => {
|
||||||
const accessToken = generateAccessToken({
|
const accessToken = generateAccessToken({
|
||||||
clientId,
|
clientId,
|
||||||
userId
|
userId
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
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'
|
||||||
@@ -101,9 +102,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.id
|
userId: dbUser.uid
|
||||||
})
|
})
|
||||||
await saveTokensInDB(dbUser.id, clientId, accessToken, 'refreshToken')
|
await saveTokensInDB(dbUser.uid, clientId, accessToken, 'refreshToken')
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/user')
|
.post('/SASjsApi/user')
|
||||||
@@ -187,7 +188,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.id}`)
|
.patch(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ ...user, displayName: newDisplayName })
|
.send({ ...user, displayName: newDisplayName })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -200,11 +201,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
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.id}`)
|
.patch(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({
|
.send({
|
||||||
displayName: newDisplayName,
|
displayName: newDisplayName,
|
||||||
@@ -221,11 +222,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser.id}`)
|
.patch(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ ...user, displayName: newDisplayName })
|
.send({ ...user, displayName: newDisplayName })
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -277,10 +278,10 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.id)
|
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
.patch(`/SASjsApi/user/${dbUser1.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -297,7 +298,7 @@ describe('user', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
.patch(`/SASjsApi/user/${dbUser1.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ username: dbUser2.username })
|
.send({ username: dbUser2.username })
|
||||||
.expect(409)
|
.expect(409)
|
||||||
@@ -325,7 +326,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -346,7 +347,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
const newDisplayName = 'My new display Name'
|
const newDisplayName = 'My new display Name'
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
@@ -372,10 +373,10 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.id)
|
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
.patch(`/SASjsApi/user/${dbUser1.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -418,7 +419,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.id}`)
|
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -428,10 +429,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.id}`)
|
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ password: user.password })
|
.send({ password: user.password })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
@@ -441,10 +442,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.id}`)
|
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(400)
|
.expect(400)
|
||||||
@@ -469,10 +470,10 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.id)
|
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser1.id}`)
|
.delete(`/SASjsApi/user/${dbUser1.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -483,10 +484,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/${dbUser.id}`)
|
.delete(`/SASjsApi/user/${dbUser.uid}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ password: 'incorrectpassword' })
|
.send({ password: 'incorrectpassword' })
|
||||||
.expect(401)
|
.expect(401)
|
||||||
@@ -510,7 +511,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
@@ -523,7 +524,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
@@ -551,7 +552,7 @@ describe('user', () => {
|
|||||||
...user,
|
...user,
|
||||||
username: 'randomUser'
|
username: 'randomUser'
|
||||||
})
|
})
|
||||||
const accessToken = await generateAndSaveToken(dbUser2.id)
|
const accessToken = await generateAndSaveToken(dbUser2.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser1.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser1.username}`)
|
||||||
@@ -565,7 +566,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.id)
|
const accessToken = await generateAndSaveToken(dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
@@ -592,7 +593,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.id
|
const userId = dbUser.uid
|
||||||
const accessToken = await generateAndSaveToken(userId)
|
const accessToken = await generateAndSaveToken(userId)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -611,7 +612,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.id
|
const userId = dbUser.uid
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${userId}`)
|
.get(`/SASjsApi/user/${userId}`)
|
||||||
@@ -634,7 +635,7 @@ describe('user', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const userId = dbUser.id
|
const userId = dbUser.uid
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${userId}`)
|
.get(`/SASjsApi/user/${userId}`)
|
||||||
@@ -652,7 +653,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.id
|
const userId = dbUser.uid
|
||||||
const accessToken = await generateAndSaveToken(userId)
|
const accessToken = await generateAndSaveToken(userId)
|
||||||
|
|
||||||
const group = {
|
const group = {
|
||||||
@@ -661,7 +662,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.groupId, dbUser.id)
|
await groupController.addUserToGroup(dbGroup.uid, dbUser.uid)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get(`/SASjsApi/user/${userId}`)
|
.get(`/SASjsApi/user/${userId}`)
|
||||||
@@ -690,8 +691,10 @@ 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/1234')
|
.get(`/SASjsApi/user/${hexValue}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(404)
|
||||||
@@ -703,7 +706,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.id
|
const userId = dbUser.uid
|
||||||
const accessToken = await generateAndSaveToken(userId)
|
const accessToken = await generateAndSaveToken(userId)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -803,13 +806,13 @@ describe('user', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
id: expect.anything(),
|
uid: expect.anything(),
|
||||||
username: adminUser.username,
|
username: adminUser.username,
|
||||||
displayName: adminUser.displayName,
|
displayName: adminUser.displayName,
|
||||||
isAdmin: adminUser.isAdmin
|
isAdmin: adminUser.isAdmin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: expect.anything(),
|
uid: expect.anything(),
|
||||||
username: user.username,
|
username: user.username,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
isAdmin: user.isAdmin
|
isAdmin: user.isAdmin
|
||||||
@@ -831,13 +834,13 @@ describe('user', () => {
|
|||||||
|
|
||||||
expect(res.body).toEqual([
|
expect(res.body).toEqual([
|
||||||
{
|
{
|
||||||
id: expect.anything(),
|
uid: expect.anything(),
|
||||||
username: adminUser.username,
|
username: adminUser.username,
|
||||||
displayName: adminUser.displayName,
|
displayName: adminUser.displayName,
|
||||||
isAdmin: adminUser.isAdmin
|
isAdmin: adminUser.isAdmin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: expect.anything(),
|
uid: expect.anything(),
|
||||||
username: 'randomUser',
|
username: 'randomUser',
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
isAdmin: user.isAdmin
|
isAdmin: user.isAdmin
|
||||||
@@ -859,10 +862,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.id)
|
return generateAndSaveToken(dbUser.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateAndSaveToken = async (userId: number) => {
|
const generateAndSaveToken = async (userId: string) => {
|
||||||
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(Number),
|
id: expect.any(String),
|
||||||
username: user.username,
|
username: user.username,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
isAdmin: user.isAdmin,
|
isAdmin: user.isAdmin,
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ stpRouter.get('/execute', async (req, res) => {
|
|||||||
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.executeGetRequest(req, query._program)
|
const response = await controller.executeGetRequest(
|
||||||
|
req,
|
||||||
|
query._program,
|
||||||
|
query._debug
|
||||||
|
)
|
||||||
|
|
||||||
if (response instanceof Buffer) {
|
if (response instanceof Buffer) {
|
||||||
res.writeHead(200, (req as any).sasHeaders)
|
res.writeHead(200, (req as any).sasHeaders)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
deleteUserValidation,
|
deleteUserValidation,
|
||||||
getUserValidation,
|
getUserValidation,
|
||||||
registerUserValidation,
|
registerUserValidation,
|
||||||
|
uidValidation,
|
||||||
updateUserValidation
|
updateUserValidation
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
@@ -56,12 +57,15 @@ userRouter.get(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
userRouter.get('/:userId', authenticateAccessToken, async (req, res) => {
|
userRouter.get('/:uid', authenticateAccessToken, async (req, res) => {
|
||||||
const { userId } = req.params
|
const { error, value: params } = uidValidation(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, parseInt(userId))
|
const response = await controller.getUser(req, uid)
|
||||||
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)
|
||||||
@@ -97,12 +101,16 @@ userRouter.patch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
userRouter.patch(
|
userRouter.patch(
|
||||||
'/:userId',
|
'/:uid',
|
||||||
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)
|
||||||
@@ -110,7 +118,7 @@ userRouter.patch(
|
|||||||
|
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
try {
|
try {
|
||||||
const response = await controller.updateUser(parseInt(userId), body)
|
const response = await controller.updateUser(uid, 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)
|
||||||
@@ -147,12 +155,16 @@ userRouter.delete(
|
|||||||
)
|
)
|
||||||
|
|
||||||
userRouter.delete(
|
userRouter.delete(
|
||||||
'/:userId',
|
'/:uid',
|
||||||
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)
|
||||||
@@ -160,7 +172,7 @@ userRouter.delete(
|
|||||||
|
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
try {
|
try {
|
||||||
await controller.deleteUser(parseInt(userId), data, user!.isAdmin)
|
await controller.deleteUser(uid, 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: number
|
userId: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface PreProgramVars {
|
export interface PreProgramVars {
|
||||||
username: string
|
username: string
|
||||||
userId: number
|
userId: string
|
||||||
displayName: string
|
displayName: string
|
||||||
serverUrl: string
|
serverUrl: string
|
||||||
httpHeaders: string[]
|
httpHeaders: string[]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface RequestUser {
|
export interface RequestUser {
|
||||||
userId: number
|
userId: string
|
||||||
clientId: string
|
clientId: string
|
||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ export interface Session {
|
|||||||
consumed: boolean
|
consumed: boolean
|
||||||
completed: boolean
|
completed: boolean
|
||||||
crashed?: string
|
crashed?: string
|
||||||
|
expiresAfterMins?: { mins: number; used: boolean }
|
||||||
}
|
}
|
||||||
|
|||||||
4
api/src/utils/crypto.ts
Normal file
4
api/src/utils/crypto.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
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 : 0,
|
userId: user ? user.userId : 'demoId',
|
||||||
displayName: user ? user.displayName : 'demo',
|
displayName: user ? user.displayName : 'demo',
|
||||||
serverUrl: protocol + host,
|
serverUrl: protocol + host,
|
||||||
httpHeaders
|
httpHeaders
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import Counter from '../model/Counter'
|
|
||||||
|
|
||||||
export const getSequenceNextValue = async (seqName: string) => {
|
|
||||||
const seqDoc = await Counter.findOne({ id: seqName })
|
|
||||||
if (!seqDoc) {
|
|
||||||
await Counter.create({ id: seqName, seq: 1 })
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
seqDoc.seq += 1
|
|
||||||
|
|
||||||
await seqDoc.save()
|
|
||||||
|
|
||||||
return seqDoc.seq
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import User from '../model/User'
|
|||||||
const isValidToken = async (
|
const isValidToken = async (
|
||||||
token: string,
|
token: string,
|
||||||
key: string,
|
key: string,
|
||||||
userId: number,
|
userId: string,
|
||||||
clientId: string
|
clientId: string
|
||||||
) => {
|
) => {
|
||||||
const promise = new Promise<boolean>((resolve, reject) =>
|
const promise = new Promise<boolean>((resolve, reject) =>
|
||||||
@@ -22,8 +22,8 @@ const isValidToken = async (
|
|||||||
return await promise.then(() => true).catch(() => false)
|
return await promise.then(() => true).catch(() => false)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTokensFromDB = async (userId: number, clientId: string) => {
|
export const getTokensFromDB = async (userId: string, 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,6 +2,7 @@ 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'
|
||||||
@@ -14,7 +15,6 @@ 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: 0,
|
userId: 'public_user_id',
|
||||||
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: number, clientId: string) => {
|
export const removeTokensInDB = async (userId: string, 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: number,
|
userId: string,
|
||||||
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: 'AllUsers',
|
name: 'all-users',
|
||||||
description: 'Group contains all users'
|
description: 'Group contains all users'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { createFolder, getAbsolutePath, getRealPath } from '@sasjs/utils'
|
import {
|
||||||
|
createFolder,
|
||||||
|
getAbsolutePath,
|
||||||
|
getRealPath,
|
||||||
|
fileExists
|
||||||
|
} from '@sasjs/utils'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
import { connectDB, getDesktopFields, ModeType, RunTimeType, SECRETS } from '.'
|
import { connectDB, getDesktopFields, ModeType, RunTimeType, SECRETS } from '.'
|
||||||
|
|
||||||
export const setProcessVariables = async () => {
|
export const setProcessVariables = async () => {
|
||||||
|
const { execPath } = process
|
||||||
|
|
||||||
|
// Check if execPath ends with 'api-macos' to determine executable for MacOS.
|
||||||
|
// This is needed to fix picking .env file issue in MacOS executable.
|
||||||
|
if (execPath) {
|
||||||
|
const envPathSplitted = execPath.split(path.sep)
|
||||||
|
|
||||||
|
if (envPathSplitted.pop() === 'api-macos') {
|
||||||
|
const envPath = path.join(envPathSplitted.join(path.sep), '.env')
|
||||||
|
|
||||||
|
// Override environment variables from envPath if file exists
|
||||||
|
if (await fileExists(envPath)) {
|
||||||
|
dotenv.config({ path: envPath, override: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { MODE, RUN_TIMES } = process.env
|
const { MODE, RUN_TIMES } = process.env
|
||||||
|
|
||||||
if (MODE === ModeType.Server) {
|
if (MODE === ModeType.Server) {
|
||||||
@@ -21,6 +43,7 @@ export const setProcessVariables = async () => {
|
|||||||
if (process.env.NODE_ENV === 'test') {
|
if (process.env.NODE_ENV === 'test') {
|
||||||
process.sasjsRoot = path.join(process.cwd(), 'sasjs_root')
|
process.sasjsRoot = path.join(process.cwd(), 'sasjs_root')
|
||||||
process.driveLoc = path.join(process.cwd(), 'sasjs_root', 'drive')
|
process.driveLoc = path.join(process.cwd(), 'sasjs_root', 'drive')
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +64,9 @@ export const setProcessVariables = async () => {
|
|||||||
|
|
||||||
const { SASJS_ROOT } = process.env
|
const { SASJS_ROOT } = process.env
|
||||||
const absPath = getAbsolutePath(SASJS_ROOT ?? 'sasjs_root', process.cwd())
|
const absPath = getAbsolutePath(SASJS_ROOT ?? 'sasjs_root', process.cwd())
|
||||||
|
|
||||||
await createFolder(absPath)
|
await createFolder(absPath)
|
||||||
|
|
||||||
process.sasjsRoot = getRealPath(absPath)
|
process.sasjsRoot = getRealPath(absPath)
|
||||||
|
|
||||||
const { DRIVE_LOCATION } = process.env
|
const { DRIVE_LOCATION } = process.env
|
||||||
@@ -49,6 +74,7 @@ export const setProcessVariables = async () => {
|
|||||||
DRIVE_LOCATION ?? path.join(process.sasjsRoot, 'drive'),
|
DRIVE_LOCATION ?? path.join(process.sasjsRoot, 'drive'),
|
||||||
process.cwd()
|
process.cwd()
|
||||||
)
|
)
|
||||||
|
|
||||||
await createFolder(absDrivePath)
|
await createFolder(absDrivePath)
|
||||||
process.driveLoc = getRealPath(absDrivePath)
|
process.driveLoc = getRealPath(absDrivePath)
|
||||||
|
|
||||||
@@ -57,7 +83,9 @@ export const setProcessVariables = async () => {
|
|||||||
LOG_LOCATION ?? path.join(process.sasjsRoot, 'logs'),
|
LOG_LOCATION ?? path.join(process.sasjsRoot, 'logs'),
|
||||||
process.cwd()
|
process.cwd()
|
||||||
)
|
)
|
||||||
|
|
||||||
await createFolder(absLogsPath)
|
await createFolder(absLogsPath)
|
||||||
|
|
||||||
process.logsLoc = getRealPath(absLogsPath)
|
process.logsLoc = getRealPath(absLogsPath)
|
||||||
|
|
||||||
process.logsUUID = 'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
|
process.logsUUID = 'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
|
||||||
|
|||||||
@@ -51,9 +51,8 @@ export const generateFileUploadSasCode = async (
|
|||||||
let fileCount = 0
|
let fileCount = 0
|
||||||
const uploadedFiles: UploadedFiles[] = []
|
const uploadedFiles: UploadedFiles[] = []
|
||||||
|
|
||||||
const sasSessionFolderList: string[] = await listFilesInFolder(
|
const sasSessionFolderList: string[] =
|
||||||
sasSessionFolder
|
await listFilesInFolder(sasSessionFolder)
|
||||||
)
|
|
||||||
sasSessionFolderList.forEach((fileName) => {
|
sasSessionFolderList.forEach((fileName) => {
|
||||||
let fileCountString = fileCount < 100 ? '0' + fileCount : fileCount
|
let fileCountString = fileCount < 100 ? '0' + fileCount : fileCount
|
||||||
fileCountString = fileCount < 10 ? '00' + fileCount : fileCount
|
fileCountString = fileCount < 10 ? '00' + fileCount : fileCount
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ 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()
|
||||||
@@ -113,7 +118,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.number().required()
|
principalId: Joi.string().length(24).hex().required()
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
export const updatePermissionValidation = (data: any): Joi.ValidationResult =>
|
export const updatePermissionValidation = (data: any): Joi.ValidationResult =>
|
||||||
@@ -178,9 +183,17 @@ export const runCodeValidation = (data: any): Joi.ValidationResult =>
|
|||||||
runTime: Joi.string().valid(...process.runTimes)
|
runTime: Joi.string().valid(...process.runTimes)
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
|
export const triggerCodeValidation = (data: any): Joi.ValidationResult =>
|
||||||
|
Joi.object({
|
||||||
|
code: Joi.string().required(),
|
||||||
|
runTime: Joi.string().valid(...process.runTimes),
|
||||||
|
expiresAfterMins: Joi.number().greater(0)
|
||||||
|
}).validate(data)
|
||||||
|
|
||||||
export const executeProgramRawValidation = (data: any): Joi.ValidationResult =>
|
export const executeProgramRawValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
Joi.object({
|
||||||
_program: Joi.string().required()
|
_program: Joi.string().required(),
|
||||||
|
_debug: Joi.number()
|
||||||
})
|
})
|
||||||
.pattern(/^/, Joi.alternatives(Joi.string(), Joi.number()))
|
.pattern(/^/, Joi.alternatives(Joi.string(), Joi.number()))
|
||||||
.validate(data)
|
.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: number,
|
userId: string,
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ import Snackbar from '@mui/material/Snackbar'
|
|||||||
import MuiAlert, { AlertProps } from '@mui/material/Alert'
|
import MuiAlert, { AlertProps } from '@mui/material/Alert'
|
||||||
import Slide, { SlideProps } from '@mui/material/Slide'
|
import Slide, { SlideProps } from '@mui/material/Slide'
|
||||||
|
|
||||||
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
||||||
props,
|
function Alert(props, ref) {
|
||||||
ref
|
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
|
||||||
) {
|
}
|
||||||
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
|
)
|
||||||
})
|
|
||||||
|
|
||||||
const Transition = (props: SlideProps) => {
|
const Transition = (props: SlideProps) => {
|
||||||
return <Slide {...props} direction="up" />
|
return <Slide {...props} direction="up" />
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ const AddPermissionModal = ({
|
|||||||
principalType: principalType.toLowerCase(),
|
principalType: principalType.toLowerCase(),
|
||||||
principalId:
|
principalId:
|
||||||
principalType.toLowerCase() === 'user'
|
principalType.toLowerCase() === 'user'
|
||||||
? userPrincipal?.id
|
? userPrincipal?.uid
|
||||||
: groupPrincipal?.groupId
|
: groupPrincipal?.uid
|
||||||
}
|
}
|
||||||
|
|
||||||
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.permissionId}>
|
<TableRow key={permission.uid}>
|
||||||
<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.permissionId}`, {
|
.patch(`/SASjsApi/permission/${permission.uid}`, {
|
||||||
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?.permissionId}`)
|
.delete(`/SASjsApi/permission/${selectedPermission?.uid}`)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
fetchPermissions()
|
fetchPermissions()
|
||||||
setSnackbarMessage('Permission deleted!')
|
setSnackbarMessage('Permission deleted!')
|
||||||
|
|||||||
@@ -62,21 +62,17 @@ const useFilterPermissions = () => {
|
|||||||
: permissions
|
: permissions
|
||||||
|
|
||||||
let filteredArray = uriFilteredPermissions.filter((permission) =>
|
let filteredArray = uriFilteredPermissions.filter((permission) =>
|
||||||
principalFilteredPermissions.some(
|
principalFilteredPermissions.some((item) => item.uid === permission.uid)
|
||||||
(item) => item.permissionId === permission.permissionId
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
filteredArray = filteredArray.filter((permission) =>
|
filteredArray = filteredArray.filter((permission) =>
|
||||||
principalTypeFilteredPermissions.some(
|
principalTypeFilteredPermissions.some(
|
||||||
(item) => item.permissionId === permission.permissionId
|
(item) => item.uid === permission.uid
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
filteredArray = filteredArray.filter((permission) =>
|
filteredArray = filteredArray.filter((permission) =>
|
||||||
settingFilteredPermissions.some(
|
settingFilteredPermissions.some((item) => item.uid === permission.uid)
|
||||||
(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?.permissionId}`, {
|
.patch(`/SASjsApi/permission/${selectedPermission?.uid}`, {
|
||||||
setting
|
setting
|
||||||
})
|
})
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
|
|||||||
@@ -26,18 +26,20 @@ const Profile = () => {
|
|||||||
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false)
|
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true)
|
if (appContext.userId) {
|
||||||
axios
|
setIsLoading(true)
|
||||||
.get(`/SASjsApi/user/${appContext.userId}`)
|
axios
|
||||||
.then((res: any) => {
|
.get(`/SASjsApi/user/${appContext.userId}`)
|
||||||
setUser(res.data)
|
.then((res: any) => {
|
||||||
})
|
setUser(res.data)
|
||||||
.catch((err) => {
|
})
|
||||||
console.log(err)
|
.catch((err) => {
|
||||||
})
|
console.log(err)
|
||||||
.finally(() => {
|
})
|
||||||
setIsLoading(false)
|
.finally(() => {
|
||||||
})
|
setIsLoading(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
}, [appContext.userId])
|
}, [appContext.userId])
|
||||||
|
|
||||||
const handleChange = (event: any) => {
|
const handleChange = (event: any) => {
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: 0.4s;
|
transition: 0.4s;
|
||||||
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 1px -1px,
|
box-shadow:
|
||||||
rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px;
|
rgba(0, 0, 0, 0.2) 0px 2px 1px -1px,
|
||||||
|
rgba(0, 0, 0, 0.14) 0px 1px 1px 0px,
|
||||||
|
rgba(0, 0, 0, 0.12) 0px 1px 3px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ChunkDetails {
|
.ChunkDetails {
|
||||||
|
|||||||
@@ -24,39 +24,32 @@ export enum RunTimeType {
|
|||||||
interface AppContextProps {
|
interface AppContextProps {
|
||||||
checkingSession: boolean
|
checkingSession: boolean
|
||||||
loggedIn: boolean
|
loggedIn: boolean
|
||||||
setLoggedIn: Dispatch<SetStateAction<boolean>> | null
|
setLoggedIn?: Dispatch<SetStateAction<boolean>>
|
||||||
needsToUpdatePassword: boolean
|
needsToUpdatePassword: boolean
|
||||||
setNeedsToUpdatePassword: Dispatch<SetStateAction<boolean>> | null
|
setNeedsToUpdatePassword?: Dispatch<SetStateAction<boolean>>
|
||||||
userId: number
|
userId?: string
|
||||||
setUserId: Dispatch<SetStateAction<number>> | null
|
setUserId?: Dispatch<SetStateAction<string | undefined>>
|
||||||
username: string
|
username: string
|
||||||
setUsername: Dispatch<SetStateAction<string>> | null
|
setUsername?: Dispatch<SetStateAction<string>>
|
||||||
displayName: string
|
displayName: string
|
||||||
setDisplayName: Dispatch<SetStateAction<string>> | null
|
setDisplayName?: Dispatch<SetStateAction<string>>
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
setIsAdmin: Dispatch<SetStateAction<boolean>> | null
|
setIsAdmin?: Dispatch<SetStateAction<boolean>>
|
||||||
mode: ModeType
|
mode: ModeType
|
||||||
runTimes: RunTimeType[]
|
runTimes: RunTimeType[]
|
||||||
logout: (() => void) | null
|
logout?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppContext = createContext<AppContextProps>({
|
export const AppContext = createContext<AppContextProps>({
|
||||||
checkingSession: false,
|
checkingSession: false,
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
setLoggedIn: null,
|
|
||||||
needsToUpdatePassword: false,
|
needsToUpdatePassword: false,
|
||||||
setNeedsToUpdatePassword: null,
|
userId: '',
|
||||||
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 }) => {
|
||||||
@@ -64,7 +57,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(0)
|
const [userId, setUserId] = useState<string>()
|
||||||
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,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ export const findExistingPermission = (
|
|||||||
) => {
|
) => {
|
||||||
for (const permission of existingPermissions) {
|
for (const permission of existingPermissions) {
|
||||||
if (
|
if (
|
||||||
permission.user?.id === newPermission.principalId &&
|
permission.user?.uid === newPermission.principalId &&
|
||||||
hasSameCombination(permission, newPermission)
|
hasSameCombination(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permission.group?.groupId === newPermission.principalId &&
|
permission.group?.uid === 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?.id === newPermission.principalId &&
|
permission.user?.uid === newPermission.principalId &&
|
||||||
hasDifferentSetting(permission, newPermission)
|
hasDifferentSetting(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permission.group?.groupId === newPermission.principalId &&
|
permission.group?.uid === newPermission.principalId &&
|
||||||
hasDifferentSetting(permission, newPermission)
|
hasDifferentSetting(permission, newPermission)
|
||||||
)
|
)
|
||||||
return permission
|
return permission
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
export interface UserResponse {
|
export interface UserResponse {
|
||||||
id: number
|
uid: string
|
||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
isAdmin: boolean
|
isAdmin: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupResponse {
|
export interface GroupResponse {
|
||||||
groupId: number
|
uid: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ export interface GroupDetailsResponse extends GroupResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PermissionResponse {
|
export interface PermissionResponse {
|
||||||
permissionId: number
|
uid: string
|
||||||
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: number
|
principalId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeNode {
|
export interface TreeNode {
|
||||||
|
|||||||
Reference in New Issue
Block a user