1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-07 06:30:06 +00:00

feat: replace ID with UID

BREAKING CHANGE: remove auto incremental ids from user, group and permissions and add a virtual uid property that returns string value of documents object id
This commit is contained in:
2023-05-09 15:01:56 +05:00
parent d2239f75c2
commit 093fe90589
36 changed files with 461 additions and 483 deletions

View File

@@ -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
@@ -285,9 +284,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 +293,7 @@ components:
isAdmin: isAdmin:
type: boolean type: boolean
required: required:
- id - uid
- username - username
- displayName - displayName
- isAdmin - isAdmin
@@ -303,32 +301,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 +332,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 +372,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 +385,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 +454,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 +467,7 @@ components:
group: group:
$ref: '#/components/schemas/GroupDetailsResponse' $ref: '#/components/schemas/GroupDetailsResponse'
required: required:
- permissionId - uid
- path - path
- type - type
- setting - setting
@@ -512,10 +506,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 +526,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:
@@ -1206,7 +1210,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 +1229,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 +1280,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 +1331,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 +1350,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 +1367,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 +1381,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 +1407,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 +1432,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 +1451,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 +1467,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 +1489,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 +1509,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 +1535,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 +1552,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 +1563,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 +1586,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 +1597,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 +1660,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 +1680,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 +1694,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 +1706,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 +1715,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 +1739,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 +1756,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
@@ -1863,7 +1850,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

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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 })
} }

View File

@@ -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(

View File

@@ -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,7 +274,7 @@ 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,
@@ -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
} }
} }

View File

@@ -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',

View File

@@ -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)

View File

@@ -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,

View File

@@ -9,7 +9,7 @@ export const verifyAdminIfNeeded: RequestHandler = (req, res, next) => {
let adminAccountRequired: boolean = true let adminAccountRequired: boolean = true
if (req.params.userId) { if (req.params.userId) {
adminAccountRequired = user?.userId !== parseInt(req.params.userId) adminAccountRequired = user?.userId !== req.params.userId
} else if (req.params.username) { } else if (req.params.username) {
adminAccountRequired = user?.username !== req.params.username adminAccountRequired = user?.username !== req.params.username
} }

View File

@@ -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,16 +39,23 @@ interface IGroup extends IGroupDocument {
} }
interface IGroupModel extends Model<IGroup> {} interface IGroupModel extends Model<IGroup> {}
const groupSchema = new Schema<IGroupDocument>({ const opts = {
toJSON: {
virtuals: true,
transform: function (doc: any, ret: any, options: any) {
delete ret._id
delete ret.id
return ret
}
}
}
const groupSchema = new Schema<IGroupDocument>(
{
name: { name: {
type: String, type: String,
required: true, required: true,
unique: true unique: true
}, },
groupId: {
type: Number,
unique: true
},
description: { description: {
type: String, type: String,
default: 'Group description.' default: 'Group description.'
@@ -60,17 +69,16 @@ const groupSchema = new Schema<IGroupDocument>({
default: true default: true
}, },
users: [{ type: Schema.Types.ObjectId, ref: 'User' }] users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
}) },
opts
)
// Hooks groupSchema.virtual('uid').get(function () {
groupSchema.pre('save', async function () { return this._id.toString()
if (this.isNew) {
this.groupId = await getSequenceNextValue('groupId')
}
}) })
groupSchema.post('save', function (group: IGroup, next: Function) { groupSchema.post('save', function (group: IGroup, next: Function) {
group.populate('users', 'id username displayName -_id').then(function () { group.populate('users', 'uid username displayName').then(function () {
next() next()
}) })
}) })

View File

@@ -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,11 +23,19 @@ 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
delete ret.id
return ret
}
}
}
const permissionSchema = new Schema<IPermissionDocument>(
{
path: { path: {
type: String, type: String,
required: true required: true
@@ -41,13 +50,12 @@ const permissionSchema = new Schema<IPermissionDocument>({
}, },
user: { type: Schema.Types.ObjectId, ref: 'User' }, user: { type: Schema.Types.ObjectId, ref: 'User' },
group: { type: Schema.Types.ObjectId, ref: 'Group' } group: { type: Schema.Types.ObjectId, ref: 'Group' }
}) },
opts
)
// Hooks permissionSchema.virtual('uid').get(function () {
permissionSchema.pre('save', async function () { return this._id.toString()
if (this.isNew) {
this.permissionId = await getSequenceNextValue('permissionId')
}
}) })
// 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[]

View File

@@ -1,4 +1,4 @@
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, getSequenceNextValue } from '../utils'
@@ -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,8 +56,19 @@ export interface IUser extends IUserDocument {
interface IUserModel extends Model<IUser> { interface IUserModel extends Model<IUser> {
hashPassword(password: string): string hashPassword(password: string): string
} }
const opts = {
toJSON: {
virtuals: true,
transform: function (doc: any, ret: any, options: any) {
delete ret._id
delete ret.id
return ret
}
}
}
const userSchema = new Schema<IUserDocument>({ const userSchema = new Schema<IUserDocument>(
{
displayName: { displayName: {
type: String, type: String,
required: true required: true
@@ -65,10 +78,6 @@ const userSchema = new Schema<IUserDocument>({
required: true, required: true,
unique: true unique: true
}, },
id: {
type: Number,
unique: true
},
password: { password: {
type: String, type: String,
required: true required: true
@@ -109,15 +118,12 @@ const userSchema = new Schema<IUserDocument>({
} }
} }
] ]
}) },
opts
)
// Hooks userSchema.virtual('uid').get(function () {
userSchema.pre('save', async function (next) { return this._id.toString()
if (this.isNew) {
this.id = await getSequenceNextValue('id')
}
next()
}) })
// Static Methods // Static Methods

View File

@@ -33,12 +33,12 @@ 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 { uid } = req.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 +56,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 +65,33 @@ 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), res.send(response)
parseInt(userId) } catch (err: any) {
) console.log('err :>> ', err)
res.status(err.code).send(err.message)
}
}
)
groupRouter.delete(
'/:groupUid/:userUid',
authenticateAccessToken,
verifyAdmin,
async (req, res) => {
const { groupUid, userUid } = req.params
const controller = new GroupController()
try {
const response = await controller.removeUserFromGroup(groupUid, userUid)
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,35 +100,15 @@ groupRouter.post(
) )
groupRouter.delete( groupRouter.delete(
'/:groupId/:userId', '/:uid',
authenticateAccessToken, authenticateAccessToken,
verifyAdmin, verifyAdmin,
async (req, res) => { async (req, res) => {
const { groupId, userId } = req.params const { uid } = req.params
const controller = new GroupController() const controller = new GroupController()
try { try {
const response = await controller.removeUserFromGroup( await controller.deleteGroup(uid)
parseInt(groupId),
parseInt(userId)
)
res.send(response)
} catch (err: any) {
res.status(err.code).send(err.message)
}
}
)
groupRouter.delete(
'/:groupId',
authenticateAccessToken,
verifyAdmin,
async (req, res) => {
const { groupId } = req.params
const controller = new GroupController()
try {
await controller.deleteGroup(parseInt(groupId))
res.status(200).send('Group Deleted!') res.status(200).send('Group Deleted!')
} catch (err: any) { } catch (err: any) {
res.status(err.code).send(err.message) res.status(err.code).send(err.message)

View File

@@ -34,14 +34,14 @@ 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 { uid } = req.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 +50,16 @@ permissionRouter.patch('/:permissionId', verifyAdmin, async (req: any, res) => {
} }
}) })
permissionRouter.delete( permissionRouter.delete('/:uid', verifyAdmin, async (req: any, res) => {
'/:permissionId', const { uid } = req.params
verifyAdmin,
async (req: any, res) => {
const { permissionId } = req.params
try { try {
await controller.deletePermission(permissionId) await controller.deletePermission(uid)
res.status(200).send('Permission Deleted!') res.status(200).send('Permission Deleted!')
} catch (err: any) { } catch (err: any) {
const statusCode = err.code const statusCode = err.code
delete err.code delete err.code
res.status(statusCode).send(err.message) res.status(statusCode).send(err.message)
} }
} })
)
export default permissionRouter export default permissionRouter

View File

@@ -56,12 +56,12 @@ userRouter.get(
} }
) )
userRouter.get('/:userId', authenticateAccessToken, async (req, res) => { userRouter.get('/:uid', authenticateAccessToken, async (req, res) => {
const { userId } = req.params const { uid } = req.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 +97,12 @@ 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 { uid } = req.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 +110,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 +147,12 @@ 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 { uid } = req.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 +160,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)

View File

@@ -1,4 +1,4 @@
export interface InfoJWT { export interface InfoJWT {
clientId: string clientId: string
userId: number userId: string
} }

View File

@@ -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[]

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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',

View File

@@ -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(

View File

@@ -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(

View File

@@ -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'
} }

View File

@@ -113,7 +113,7 @@ export const registerPermissionValidation = (data: any): Joi.ValidationResult =>
principalType: Joi.string() principalType: Joi.string()
.required() .required()
.valid(...Object.values(PrincipalType)), .valid(...Object.values(PrincipalType)),
principalId: Joi.number().required() principalId: Joi.string().required()
}).validate(data) }).validate(data)
export const updatePermissionValidation = (data: any): Joi.ValidationResult => export const updatePermissionValidation = (data: any): Joi.ValidationResult =>

View File

@@ -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
@@ -26,7 +26,7 @@ export const verifyTokenInDB = async (
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

View File

@@ -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)

View File

@@ -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>

View File

@@ -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) => {

View File

@@ -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!')

View File

@@ -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)

View File

@@ -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) => {

View File

@@ -27,8 +27,8 @@ interface AppContextProps {
setLoggedIn: Dispatch<SetStateAction<boolean>> | null setLoggedIn: Dispatch<SetStateAction<boolean>> | null
needsToUpdatePassword: boolean needsToUpdatePassword: boolean
setNeedsToUpdatePassword: Dispatch<SetStateAction<boolean>> | null setNeedsToUpdatePassword: Dispatch<SetStateAction<boolean>> | null
userId: number userId: string
setUserId: Dispatch<SetStateAction<number>> | null setUserId: Dispatch<SetStateAction<string>> | null
username: string username: string
setUsername: Dispatch<SetStateAction<string>> | null setUsername: Dispatch<SetStateAction<string>> | null
displayName: string displayName: string
@@ -46,7 +46,7 @@ export const AppContext = createContext<AppContextProps>({
setLoggedIn: null, setLoggedIn: null,
needsToUpdatePassword: false, needsToUpdatePassword: false,
setNeedsToUpdatePassword: null, setNeedsToUpdatePassword: null,
userId: 0, userId: '',
setUserId: null, setUserId: null,
username: '', username: '',
setUsername: null, setUsername: null,
@@ -64,7 +64,7 @@ const AppContextProvider = (props: { children: ReactNode }) => {
const [checkingSession, setCheckingSession] = useState(false) const [checkingSession, setCheckingSession] = useState(false)
const [loggedIn, setLoggedIn] = useState(false) const [loggedIn, setLoggedIn] = useState(false)
const [needsToUpdatePassword, setNeedsToUpdatePassword] = useState(false) const [needsToUpdatePassword, setNeedsToUpdatePassword] = useState(false)
const [userId, setUserId] = useState(0) const [userId, setUserId] = useState('')
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)

View File

@@ -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

View File

@@ -1,12 +1,12 @@
export interface UserResponse { export interface UserResponse {
id: number uid: number
username: string username: string
displayName: string displayName: string
isAdmin: boolean isAdmin: boolean
} }
export interface GroupResponse { export interface GroupResponse {
groupId: number uid: number
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: number
path: string path: string
type: string type: string
setting: string setting: string