diff --git a/api/public/swagger.yaml b/api/public/swagger.yaml index 66d14e81..023adaad 100644 --- a/api/public/swagger.yaml +++ b/api/public/swagger.yaml @@ -40,8 +40,7 @@ components: clientId: type: string userId: - type: number - format: double + type: string required: - clientId - userId @@ -285,9 +284,8 @@ components: additionalProperties: false UserResponse: properties: - id: - type: number - format: double + uid: + type: string username: type: string displayName: @@ -295,7 +293,7 @@ components: isAdmin: type: boolean required: - - id + - uid - username - displayName - isAdmin @@ -303,32 +301,30 @@ components: additionalProperties: false GroupResponse: properties: - groupId: - type: number - format: double + uid: + type: string name: type: string description: type: string required: - - groupId + - uid - name - description type: object additionalProperties: false UserDetailsResponse: properties: - id: - type: number - format: double - displayName: + uid: type: string username: type: string - isActive: - type: boolean + displayName: + type: string isAdmin: type: boolean + isActive: + type: boolean autoExec: type: string groups: @@ -336,11 +332,11 @@ components: $ref: '#/components/schemas/GroupResponse' type: array required: - - id - - displayName + - uid - username - - isActive + - displayName - isAdmin + - isActive type: object additionalProperties: false UserPayload: @@ -376,9 +372,8 @@ components: additionalProperties: false GroupDetailsResponse: properties: - groupId: - type: number - format: double + uid: + type: string name: type: string description: @@ -390,7 +385,7 @@ components: $ref: '#/components/schemas/UserResponse' type: array required: - - groupId + - uid - name - description - isActive @@ -459,9 +454,8 @@ components: additionalProperties: false PermissionDetailsResponse: properties: - permissionId: - type: number - format: double + uid: + type: string path: type: string type: @@ -473,7 +467,7 @@ components: group: $ref: '#/components/schemas/GroupDetailsResponse' required: - - permissionId + - uid - path - type - setting @@ -512,10 +506,8 @@ components: description: 'Indicates the type of principal' example: user principalId: - type: number - format: double + type: string description: 'The id of user or group to which a rule is assigned.' - example: 123 required: - path - type @@ -534,25 +526,37 @@ components: - setting type: object additionalProperties: false + Pick_UserResponse.Exclude_keyofUserResponse.uid__: + properties: + username: + type: string + displayName: + type: string + isAdmin: + type: boolean + required: + - username + - displayName + - isAdmin + type: object + description: 'From T, pick a set of properties whose keys are in the union K' SessionResponse: properties: - id: - type: number - format: double username: type: string displayName: type: string isAdmin: type: boolean + id: + type: string needsToUpdatePassword: type: boolean required: - - id - username - displayName - isAdmin - - needsToUpdatePassword + - id type: object additionalProperties: false ExecutePostRequestPayload: @@ -1206,7 +1210,7 @@ paths: type: array examples: '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.' tags: - User @@ -1225,7 +1229,7 @@ paths: $ref: '#/components/schemas/UserDetailsResponse' examples: '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.' tags: - User @@ -1276,7 +1280,7 @@ paths: $ref: '#/components/schemas/UserDetailsResponse' examples: '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.' tags: - User @@ -1327,7 +1331,7 @@ paths: password: type: string type: object - '/SASjsApi/user/{userId}': + '/SASjsApi/user/{uid}': get: operationId: GetUser responses: @@ -1346,14 +1350,12 @@ paths: bearerAuth: [] parameters: - - description: 'The user''s identifier' in: path - name: userId + name: uid required: true schema: - format: double - type: number - example: 1234 + type: string + '/SASjsApi/user/{userId}': patch: operationId: UpdateUser responses: @@ -1365,7 +1367,7 @@ paths: $ref: '#/components/schemas/UserDetailsResponse' examples: '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.' tags: - User @@ -1379,8 +1381,7 @@ paths: name: userId required: true schema: - format: double - type: number + type: string example: '1234' requestBody: required: true @@ -1406,8 +1407,7 @@ paths: name: userId required: true schema: - format: double - type: number + type: string example: 1234 requestBody: required: true @@ -1432,7 +1432,7 @@ paths: type: array examples: '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.' tags: - Group @@ -1451,7 +1451,7 @@ paths: $ref: '#/components/schemas/GroupDetailsResponse' examples: '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.' tags: - Group @@ -1467,7 +1467,7 @@ paths: $ref: '#/components/schemas/GroupPayload' '/SASjsApi/group/by/groupname/{name}': get: - operationId: GetGroupByGroupName + operationId: GetGroupByName responses: '200': description: Ok @@ -1489,7 +1489,7 @@ paths: required: true schema: type: string - '/SASjsApi/group/{groupId}': + '/SASjsApi/group/{uid}': get: operationId: GetGroup responses: @@ -1509,12 +1509,11 @@ paths: - description: 'The group''s identifier' in: path - name: groupId + name: uid required: true schema: - format: double - type: number - example: 1234 + type: string + example: 12ByteString delete: operationId: DeleteGroup responses: @@ -1536,13 +1535,12 @@ paths: - description: 'The group''s identifier' in: path - name: groupId + name: uid required: true schema: - format: double - type: number - example: 1234 - '/SASjsApi/group/{groupId}/{userId}': + type: string + example: 12ByteString + '/SASjsApi/group/{groupUid}/{userUid}': post: operationId: AddUserToGroup responses: @@ -1554,7 +1552,7 @@ paths: $ref: '#/components/schemas/GroupDetailsResponse' examples: '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.' tags: - Group @@ -1565,21 +1563,18 @@ paths: - description: 'The group''s identifier' in: path - name: groupId + name: groupUid required: true schema: - format: double - type: number - example: '1234' + type: string + example: 12ByteString - description: 'The user''s identifier' in: path - name: userId + name: userUid required: true schema: - format: double - type: number - example: '6789' + type: string delete: operationId: RemoveUserFromGroup responses: @@ -1591,8 +1586,8 @@ paths: $ref: '#/components/schemas/GroupDetailsResponse' examples: 'Example 1': - value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - summary: 'Remove a user to a group. Admin task only.' + value: {uid: groupIdString, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} + summary: 'Remove a user from a group. Admin task only.' tags: - Group security: @@ -1602,21 +1597,19 @@ paths: - description: 'The group''s identifier' in: path - name: groupId + name: groupUid required: true schema: - format: double - type: number - example: '1234' + type: string + example: 12ByteString - description: 'The user''s identifier' in: path - name: userId + name: userUid required: true schema: - format: double - type: number - example: '6789' + type: string + example: 12ByteString /SASjsApi/info: get: operationId: Info @@ -1667,7 +1660,7 @@ paths: type: array examples: '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." summary: 'Get the list of permission rules. If the user is admin, all rules are returned.' tags: @@ -1687,7 +1680,7 @@ paths: $ref: '#/components/schemas/PermissionDetailsResponse' examples: '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.' tags: - Permission @@ -1701,7 +1694,7 @@ paths: application/json: schema: $ref: '#/components/schemas/RegisterPermissionPayload' - '/SASjsApi/permission/{permissionId}': + '/SASjsApi/permission/{uid}': patch: operationId: UpdatePermission responses: @@ -1713,7 +1706,7 @@ paths: $ref: '#/components/schemas/PermissionDetailsResponse' examples: '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' tags: - Permission @@ -1722,14 +1715,11 @@ paths: bearerAuth: [] parameters: - - description: 'The permission''s identifier' in: path - name: permissionId + name: uid required: true schema: - format: double - type: number - example: 1234 + type: string requestBody: required: true content: @@ -1749,14 +1739,11 @@ paths: bearerAuth: [] parameters: - - description: 'The user''s identifier' in: path - name: permissionId + name: uid required: true schema: - format: double - type: number - example: 1234 + type: string /SASjsApi/session: get: operationId: Session @@ -1769,7 +1756,7 @@ paths: $ref: '#/components/schemas/SessionResponse' examples: '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).' tags: - Session @@ -1863,7 +1850,7 @@ paths: application/json: schema: 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} required: - user diff --git a/api/src/controllers/auth.ts b/api/src/controllers/auth.ts index 6c56e898..5869204e 100644 --- a/api/src/controllers/auth.ts +++ b/api/src/controllers/auth.ts @@ -27,14 +27,14 @@ import User from '../model/User' @Tags('Auth') export class AuthController { 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]) return (AuthController.authCodes[userId][clientId] = code) AuthController.authCodes[userId] = { [clientId]: code } return AuthController.authCodes[userId][clientId] } - static deleteCode = (userId: number, clientId: string) => + static deleteCode = (userId: string, clientId: string) => delete AuthController.authCodes[userId][clientId] /** @@ -159,7 +159,7 @@ const updatePassword = async ( ) => { const { currentPassword, newPassword } = data const userId = req.user?.userId - const dbUser = await User.findOne({ id: userId }) + const dbUser = await User.findOne({ _id: userId }) if (!dbUser) throw { diff --git a/api/src/controllers/group.ts b/api/src/controllers/group.ts index 3a49f43a..9119fda4 100644 --- a/api/src/controllers/group.ts +++ b/api/src/controllers/group.ts @@ -12,28 +12,29 @@ import { import Group, { GroupPayload, PUBLIC_GROUP_NAME } from '../model/Group' import User from '../model/User' -import { AuthProviderType } from '../utils' -import { UserResponse } from './user' +import { GetUserBy, UserResponse } from './user' export interface GroupResponse { - groupId: number + uid: string name: string description: string } -export interface GroupDetailsResponse { - groupId: number - name: string - description: string +export interface GroupDetailsResponse extends GroupResponse { isActive: boolean users: UserResponse[] } interface GetGroupBy { - groupId?: number + _id?: string name?: string } +enum GroupAction { + AddUser = 'addUser', + RemoveUser = 'removeUser' +} + @Security('bearerAuth') @Route('SASjsApi/group') @Tags('Group') @@ -44,7 +45,7 @@ export class GroupController { */ @Example([ { - groupId: 123, + uid: 'groupIdString', name: 'DCGroup', description: 'This group represents Data Controller Users' } @@ -59,7 +60,7 @@ export class GroupController { * */ @Example({ - groupId: 123, + uid: 'groupIdString', name: 'DCGroup', description: 'This group represents Data Controller Users', isActive: true, @@ -78,7 +79,7 @@ export class GroupController { * @example dcgroup */ @Get('by/groupname/{name}') - public async getGroupByGroupName( + public async getGroupByName( @Path() name: string ): Promise { return getGroup({ name }) @@ -86,68 +87,66 @@ export class GroupController { /** * @summary Get list of members of a group (userName). All users can request this. - * @param groupId The group's identifier - * @example groupId 1234 + * @param uid The group's identifier + * @example uid "12ByteString" */ - @Get('{groupId}') - public async getGroup( - @Path() groupId: number - ): Promise { - return getGroup({ groupId }) + @Get('{uid}') + public async getGroup(@Path() uid: string): Promise { + return getGroup({ _id: uid }) } /** * @summary Add a user to a group. Admin task only. - * @param groupId The group's identifier - * @example groupId "1234" - * @param userId The user's identifier - * @example userId "6789" + * @param groupUid The group's identifier + * @example groupUid "12ByteString" + * @param userUid The user's identifier + * @example userId "12ByteString" */ @Example({ - groupId: 123, + uid: 'groupIdString', name: 'DCGroup', description: 'This group represents Data Controller Users', isActive: true, users: [] }) - @Post('{groupId}/{userId}') + @Post('{groupUid}/{userUid}') public async addUserToGroup( - @Path() groupId: number, - @Path() userId: number + @Path() groupUid: string, + @Path() userUid: string ): Promise { - return addUserToGroup(groupId, userId) + return addUserToGroup(groupUid, userUid) } /** - * @summary Remove a user to a group. Admin task only. - * @param groupId The group's identifier - * @example groupId "1234" - * @param userId The user's identifier - * @example userId "6789" + * @summary Remove a user from a group. Admin task only. + * @param groupUid The group's identifier + * @example groupUid "12ByteString" + * @param userUid The user's identifier + * @example userUid "12ByteString" */ @Example({ - groupId: 123, + uid: 'groupIdString', name: 'DCGroup', description: 'This group represents Data Controller Users', isActive: true, users: [] }) - @Delete('{groupId}/{userId}') + @Delete('{groupUid}/{userUid}') public async removeUserFromGroup( - @Path() groupId: number, - @Path() userId: number + @Path() groupUid: string, + @Path() userUid: string ): Promise { - return removeUserFromGroup(groupId, userId) + return removeUserFromGroup(groupUid, userUid) } /** * @summary Delete a group. Admin task only. - * @param groupId The group's identifier - * @example groupId 1234 + * @param uid The group's identifier + * @example uid "12ByteString" */ - @Delete('{groupId}') - public async deleteGroup(@Path() groupId: number) { - const group = await Group.findOne({ groupId }) + @Delete('{uid}') + public async deleteGroup(@Path() uid: string) { + const group = await Group.findOne({ _id: uid }) if (!group) throw { code: 404, @@ -160,9 +159,7 @@ export class GroupController { } const getAllGroups = async (): Promise => - await Group.find({}) - .select({ _id: 0, groupId: 1, name: 1, description: 1 }) - .exec() + await Group.find({}).select('uid name description').exec() const createGroup = async ({ name, @@ -187,7 +184,7 @@ const createGroup = async ({ const savedGroup = await group.save() return { - groupId: savedGroup.groupId, + uid: savedGroup.uid, name: savedGroup.name, description: savedGroup.description, isActive: savedGroup.isActive, @@ -198,11 +195,12 @@ const createGroup = async ({ const getGroup = async (findBy: GetGroupBy): Promise => { const group = (await Group.findOne( findBy, - 'groupId name description isActive users -_id' + 'uid name description isActive users' ).populate( 'users', - 'id username displayName isAdmin -_id' + 'uid username displayName isAdmin' )) as unknown as GroupDetailsResponse + if (!group) throw { code: 404, @@ -211,7 +209,7 @@ const getGroup = async (findBy: GetGroupBy): Promise => { } return { - groupId: group.groupId, + uid: group.uid, name: group.name, description: group.description, isActive: group.isActive, @@ -220,23 +218,23 @@ const getGroup = async (findBy: GetGroupBy): Promise => { } const addUserToGroup = async ( - groupId: number, - userId: number + groupUid: string, + userUid: string ): Promise => - updateUsersListInGroup(groupId, userId, 'addUser') + updateUsersListInGroup(groupUid, userUid, GroupAction.AddUser) const removeUserFromGroup = async ( - groupId: number, - userId: number + groupUid: string, + userUid: string ): Promise => - updateUsersListInGroup(groupId, userId, 'removeUser') + updateUsersListInGroup(groupUid, userUid, GroupAction.RemoveUser) const updateUsersListInGroup = async ( - groupId: number, - userId: number, - action: 'addUser' | 'removeUser' + groupUid: string, + userUid: string, + action: GroupAction ): Promise => { - const group = await Group.findOne({ groupId }) + const group = await Group.findOne({ _id: groupUid }) if (!group) throw { code: 404, @@ -258,7 +256,7 @@ const updateUsersListInGroup = async ( 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) throw { code: 404, @@ -274,7 +272,7 @@ const updateUsersListInGroup = async ( } const updatedGroup = - action === 'addUser' + action === GroupAction.AddUser ? await group.addUser(user) : await group.removeUser(user) @@ -286,7 +284,7 @@ const updateUsersListInGroup = async ( } return { - groupId: updatedGroup.groupId, + uid: updatedGroup.uid, name: updatedGroup.name, description: updatedGroup.description, isActive: updatedGroup.isActive, diff --git a/api/src/controllers/permission.ts b/api/src/controllers/permission.ts index 9f77cf2c..be2b1161 100644 --- a/api/src/controllers/permission.ts +++ b/api/src/controllers/permission.ts @@ -56,9 +56,9 @@ interface RegisterPermissionPayload { principalType: PrincipalType /** * The id of user or group to which a rule is assigned. - * @example 123 + * @example 'groupIdString' */ - principalId: number + principalId: string } interface UpdatePermissionPayload { @@ -70,7 +70,7 @@ interface UpdatePermissionPayload { } export interface PermissionDetailsResponse { - permissionId: number + uid: string path: string type: string setting: string @@ -91,24 +91,24 @@ export class PermissionController { */ @Example([ { - permissionId: 123, + uid: 'permissionId1String', path: '/SASjsApi/code/execute', type: 'Route', setting: 'Grant', user: { - id: 1, + uid: 'user1-id', username: 'johnSnow01', displayName: 'John Snow', isAdmin: false } }, { - permissionId: 124, + uid: 'permissionId2String', path: '/SASjsApi/code/execute', type: 'Route', setting: 'Grant', group: { - groupId: 1, + uid: 'group1-id', name: 'DCGroup', description: 'This group represents Data Controller Users', isActive: true, @@ -128,12 +128,12 @@ export class PermissionController { * */ @Example({ - permissionId: 123, + uid: 'permissionIdString', path: '/SASjsApi/code/execute', type: 'Route', setting: 'Grant', user: { - id: 1, + uid: 'userIdString', username: 'johnSnow01', displayName: 'John Snow', isAdmin: false @@ -149,36 +149,36 @@ export class PermissionController { /** * @summary Update permission setting. Admin only * @param permissionId The permission's identifier - * @example permissionId 1234 + * @example permissionId "permissionIdString" */ @Example({ - permissionId: 123, + uid: 'permissionIdString', path: '/SASjsApi/code/execute', type: 'Route', setting: 'Grant', user: { - id: 1, + uid: 'userIdString', username: 'johnSnow01', displayName: 'John Snow', isAdmin: false } }) - @Patch('{permissionId}') + @Patch('{uid}') public async updatePermission( - @Path() permissionId: number, + @Path() uid: string, @Body() body: UpdatePermissionPayload ): Promise { - return updatePermission(permissionId, body) + return updatePermission(uid, body) } /** * @summary Delete a permission. Admin only. * @param permissionId The user's identifier - * @example permissionId 1234 + * @example permissionId "permissionIdString" */ - @Delete('{permissionId}') - public async deletePermission(@Path() permissionId: number) { - return deletePermission(permissionId) + @Delete('{uid}') + public async deletePermission(@Path() uid: string) { + return deletePermission(uid) } } @@ -191,7 +191,7 @@ const getAllPermissions = async ( else { const permissions: PermissionDetailsResponse[] = [] - const dbUser = await User.findOne({ id: user?.userId }) + const dbUser = await User.findOne({ _id: user?.userId }) if (!dbUser) throw { code: 404, @@ -227,7 +227,7 @@ const createPermission = async ({ switch (principalType) { case PrincipalType.user: { - const userInDB = await User.findOne({ id: principalId }) + const userInDB = await User.findOne({ _id: principalId }) if (!userInDB) throw { code: 404, @@ -259,7 +259,7 @@ const createPermission = async ({ permission.user = userInDB._id user = { - id: userInDB.id, + uid: userInDB.uid, username: userInDB.username, displayName: userInDB.displayName, isAdmin: userInDB.isAdmin @@ -267,7 +267,7 @@ const createPermission = async ({ break } case PrincipalType.group: { - const groupInDB = await Group.findOne({ groupId: principalId }) + const groupInDB = await Group.findOne({ _id: principalId }) if (!groupInDB) throw { code: 404, @@ -291,13 +291,13 @@ const createPermission = async ({ permission.group = groupInDB._id group = { - groupId: groupInDB.groupId, + uid: groupInDB.uid, name: groupInDB.name, description: groupInDB.description, isActive: groupInDB.isActive, users: groupInDB.populate({ path: 'users', - select: 'id username displayName isAdmin -_id', + select: 'uid username displayName isAdmin -_id', options: { limit: 15 } }) as unknown as UserResponse[] } @@ -314,7 +314,7 @@ const createPermission = async ({ const savedPermission = await permission.save() return { - permissionId: savedPermission.permissionId, + uid: savedPermission.uid, path: savedPermission.path, type: savedPermission.type, setting: savedPermission.setting, @@ -324,27 +324,21 @@ const createPermission = async ({ } const updatePermission = async ( - id: number, + uid: string, data: UpdatePermissionPayload ): Promise => { const { setting } = data const updatedPermission = (await Permission.findOneAndUpdate( - { permissionId: id }, + { _id: uid }, { setting }, { new: true } ) - .select({ - _id: 0, - permissionId: 1, - path: 1, - type: 1, - setting: 1 - }) - .populate({ path: 'user', select: 'id username displayName isAdmin -_id' }) + .select('uid path type setting') + .populate({ path: 'user', select: 'uid username displayName isAdmin' }) .populate({ path: 'group', - select: 'groupId name description -_id' + select: 'groupId name description' })) as unknown as PermissionDetailsResponse if (!updatedPermission) throw { @@ -356,13 +350,13 @@ const updatePermission = async ( return updatedPermission } -const deletePermission = async (id: number) => { - const permission = await Permission.findOne({ permissionId: id }) +const deletePermission = async (uid: string) => { + const permission = await Permission.findOne({ _id: uid }) if (!permission) throw { code: 404, status: 'Not Found', message: 'Permission not found.' } - await Permission.deleteOne({ permissionId: id }) + await Permission.deleteOne({ _id: uid }) } diff --git a/api/src/controllers/session.ts b/api/src/controllers/session.ts index f0cd049d..328a94ab 100644 --- a/api/src/controllers/session.ts +++ b/api/src/controllers/session.ts @@ -2,8 +2,9 @@ import express from 'express' import { Request, Security, Route, Tags, Example, Get } from 'tsoa' import { UserResponse } from './user' -interface SessionResponse extends UserResponse { - needsToUpdatePassword: boolean +interface SessionResponse extends Omit { + id: string + needsToUpdatePassword?: boolean } @Security('bearerAuth') @@ -14,11 +15,12 @@ export class SessionController { * @summary Get session info (username). * */ - @Example({ - id: 123, + @Example({ + id: 'userIdString', username: 'johnusername', displayName: 'John', - isAdmin: false + isAdmin: false, + needsToUpdatePassword: false }) @Get('/') public async session( diff --git a/api/src/controllers/user.ts b/api/src/controllers/user.ts index 424df900..1923e1b8 100644 --- a/api/src/controllers/user.ts +++ b/api/src/controllers/user.ts @@ -26,18 +26,14 @@ import { import { GroupController, GroupResponse } from './group' export interface UserResponse { - id: number + uid: string username: string displayName: string isAdmin: boolean } -export interface UserDetailsResponse { - id: number - displayName: string - username: string +export interface UserDetailsResponse extends UserResponse { isActive: boolean - isAdmin: boolean autoExec?: string groups?: GroupResponse[] } @@ -52,13 +48,13 @@ export class UserController { */ @Example([ { - id: 123, + uid: 'userIdString', username: 'johnusername', displayName: 'John', isAdmin: false }, { - id: 456, + uid: 'anotherUserIdString', username: 'starkusername', displayName: 'Stark', isAdmin: true @@ -74,7 +70,7 @@ export class UserController { * */ @Example({ - id: 1234, + uid: 'userIdString', displayName: 'John Snow', username: 'johnSnow01', isAdmin: false, @@ -111,20 +107,20 @@ export class UserController { * Only Admin or user itself will get user autoExec code. * @summary Get user properties - such as group memberships, userName, displayName. * @param userId The user's identifier - * @example userId 1234 + * @example userId "userIdString" */ - @Get('{userId}') + @Get('{uid}') public async getUser( @Request() req: express.Request, - @Path() userId: number + @Path() uid: string ): Promise { const { MODE } = process.env if (MODE === ModeType.Desktop) return getDesktopAutoExec() const { user } = req - const getAutoExec = user!.isAdmin || user!.userId == userId - return getUser({ id: userId }, getAutoExec) + const getAutoExec = user!.isAdmin || user!.userId === uid + return getUser({ _id: uid }, getAutoExec) } /** @@ -133,7 +129,7 @@ export class UserController { * @example username "johnSnow01" */ @Example({ - id: 1234, + uid: 'userIdString', displayName: 'John Snow', username: 'johnSnow01', isAdmin: false, @@ -158,7 +154,7 @@ export class UserController { * @example userId "1234" */ @Example({ - id: 1234, + uid: 'userIdString', displayName: 'John Snow', username: 'johnSnow01', isAdmin: false, @@ -166,7 +162,7 @@ export class UserController { }) @Patch('{userId}') public async updateUser( - @Path() userId: number, + @Path() userId: string, @Body() body: UserPayload ): Promise { const { MODE } = process.env @@ -174,7 +170,7 @@ export class UserController { if (MODE === ModeType.Desktop) return updateDesktopAutoExec(body.autoExec ?? '') - return updateUser({ id: userId }, body) + return updateUser({ _id: userId }, body) } /** @@ -198,18 +194,16 @@ export class UserController { */ @Delete('{userId}') public async deleteUser( - @Path() userId: number, + @Path() userId: string, @Body() body: { password?: string }, @Query() @Hidden() isAdmin: boolean = false ) { - return deleteUser({ id: userId }, isAdmin, body) + return deleteUser({ _id: userId }, isAdmin, body) } } const getAllUsers = async (): Promise => - await User.find({}) - .select({ _id: 0, id: 1, username: 1, displayName: 1, isAdmin: 1 }) - .exec() + await User.find({}).select('uid username displayName isAdmin').exec() const createUser = async (data: UserPayload): Promise => { const { displayName, username, password, isAdmin, isActive, autoExec } = data @@ -239,15 +233,15 @@ const createUser = async (data: UserPayload): Promise => { const groupController = new GroupController() const allUsersGroup = await groupController - .getGroupByGroupName(ALL_USERS_GROUP.name) + .getGroupByName(ALL_USERS_GROUP.name) .catch(() => {}) if (allUsersGroup) { - await groupController.addUserToGroup(allUsersGroup.groupId, savedUser.id) + await groupController.addUserToGroup(allUsersGroup.uid, savedUser.uid) } return { - id: savedUser.id, + uid: savedUser.uid, displayName: savedUser.displayName, username: savedUser.username, isActive: savedUser.isActive, @@ -256,8 +250,8 @@ const createUser = async (data: UserPayload): Promise => { } } -interface GetUserBy { - id?: number +export interface GetUserBy { + _id?: string username?: string } @@ -267,10 +261,10 @@ const getUser = async ( ): Promise => { const user = (await User.findOne( findBy, - `id displayName username isActive isAdmin autoExec -_id` + `uid displayName username isActive isAdmin autoExec` ).populate( 'groups', - 'groupId name description -_id' + 'uid name description' )) as unknown as UserDetailsResponse if (!user) @@ -280,7 +274,7 @@ const getUser = async ( } return { - id: user.id, + uid: user.uid, displayName: user.displayName, username: user.username, isActive: user.isActive, @@ -293,7 +287,7 @@ const getUser = async ( const getDesktopAutoExec = async () => { return { ...desktopUser, - id: desktopUser.userId, + uid: desktopUser.userId, autoExec: await getUserAutoExec() } } @@ -329,8 +323,8 @@ const updateUser = async ( const usernameExist = await User.findOne({ username }) if (usernameExist) { if ( - (findBy.id && usernameExist.id != findBy.id) || - (findBy.username && usernameExist.username != findBy.username) + (findBy._id && usernameExist.uid !== findBy._id) || + (findBy.username && usernameExist.username !== findBy.username) ) throw { code: 409, @@ -350,11 +344,11 @@ const updateUser = async ( if (!updatedUser) throw { code: 404, - message: `Unable to find user with ${findBy.id || findBy.username}` + message: `Unable to find user with ${findBy._id || findBy.username}` } return { - id: updatedUser.id, + uid: updatedUser.uid, username: updatedUser.username, displayName: updatedUser.displayName, isAdmin: updatedUser.isAdmin, @@ -367,7 +361,7 @@ const updateDesktopAutoExec = async (autoExec: string) => { await updateUserAutoExec(autoExec) return { ...desktopUser, - id: desktopUser.userId, + uid: desktopUser.userId, autoExec } } diff --git a/api/src/middlewares/authenticateToken.ts b/api/src/middlewares/authenticateToken.ts index f0da345b..c2475aef 100644 --- a/api/src/middlewares/authenticateToken.ts +++ b/api/src/middlewares/authenticateToken.ts @@ -76,7 +76,7 @@ const authenticateToken = async ( const { MODE } = process.env if (MODE === ModeType.Desktop) { req.user = { - userId: 1234, + userId: '1234', clientId: 'desktopModeClientId', username: 'desktopModeUsername', displayName: 'desktopModeDisplayName', diff --git a/api/src/middlewares/authorize.ts b/api/src/middlewares/authorize.ts index 105354c9..4f0bbc18 100644 --- a/api/src/middlewares/authorize.ts +++ b/api/src/middlewares/authorize.ts @@ -18,7 +18,7 @@ export const authorize: RequestHandler = async (req, res, next) => { // no need to check for permissions when route is Public 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) const path = getPath(req) diff --git a/api/src/middlewares/desktop.ts b/api/src/middlewares/desktop.ts index 3b352f4d..862f6c6d 100644 --- a/api/src/middlewares/desktop.ts +++ b/api/src/middlewares/desktop.ts @@ -28,7 +28,7 @@ export const desktopRestrict: RequestHandler = (req, res, next) => { } export const desktopUser: RequestUser = { - userId: 12345, + userId: '12345', clientId: 'desktop_app', username: userInfo().username, displayName: userInfo().username, diff --git a/api/src/middlewares/verifyAdminIfNeeded.ts b/api/src/middlewares/verifyAdminIfNeeded.ts index c9246f6e..de39eacd 100644 --- a/api/src/middlewares/verifyAdminIfNeeded.ts +++ b/api/src/middlewares/verifyAdminIfNeeded.ts @@ -8,8 +8,8 @@ export const verifyAdminIfNeeded: RequestHandler = (req, res, next) => { if (!user?.isAdmin) { let adminAccountRequired: boolean = true - if (req.params.userId) { - adminAccountRequired = user?.userId !== parseInt(req.params.userId) + if (req.params.uid) { + adminAccountRequired = user?.userId !== req.params.uid } else if (req.params.username) { adminAccountRequired = user?.username !== req.params.username } diff --git a/api/src/model/Counter.ts b/api/src/model/Counter.ts deleted file mode 100644 index 1dfac3b4..00000000 --- a/api/src/model/Counter.ts +++ /dev/null @@ -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) diff --git a/api/src/model/Group.ts b/api/src/model/Group.ts index f3cccc6e..5d698f2e 100644 --- a/api/src/model/Group.ts +++ b/api/src/model/Group.ts @@ -1,9 +1,9 @@ import { Schema, model, Document, Model } from 'mongoose' import { GroupDetailsResponse } from '../controllers' 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 { /** @@ -24,10 +24,12 @@ export interface GroupPayload { } interface IGroupDocument extends GroupPayload, Document { - groupId: number isActive: boolean users: Schema.Types.ObjectId[] authProvider?: AuthProviderType + + // Declare virtual properties as read-only properties + readonly uid: string } interface IGroup extends IGroupDocument { @@ -37,40 +39,46 @@ interface IGroup extends IGroupDocument { } interface IGroupModel extends Model {} -const groupSchema = new Schema({ - name: { - type: String, - required: true, - unique: true - }, - groupId: { - type: Number, - unique: true - }, - description: { - type: String, - default: 'Group description.' - }, - authProvider: { - type: String, - enum: AuthProviderType - }, - isActive: { - type: Boolean, - default: true +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( + { + 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' }] }, - users: [{ type: Schema.Types.ObjectId, ref: 'User' }] -}) + opts +) -// Hooks -groupSchema.pre('save', async function () { - if (this.isNew) { - this.groupId = await getSequenceNextValue('groupId') - } +groupSchema.virtual('uid').get(function () { + return this._id.toString() }) 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() }) }) diff --git a/api/src/model/Permission.ts b/api/src/model/Permission.ts index 77d0f196..fc20a29d 100644 --- a/api/src/model/Permission.ts +++ b/api/src/model/Permission.ts @@ -1,6 +1,5 @@ import { Schema, model, Document, Model } from 'mongoose' import { PermissionDetailsResponse } from '../controllers' -import { getSequenceNextValue } from '../utils' interface GetPermissionBy { user?: Schema.Types.ObjectId @@ -11,9 +10,11 @@ interface IPermissionDocument extends Document { path: string type: string setting: string - permissionId: number user: Schema.Types.ObjectId group: Schema.Types.ObjectId + + // Declare virtual properties as read-only properties + readonly uid: string } interface IPermission extends IPermissionDocument {} @@ -22,32 +23,39 @@ interface IPermissionModel extends Model { get(getBy: GetPermissionBy): Promise } -const permissionSchema = new Schema({ - permissionId: { - type: Number, - unique: true - }, - path: { - type: String, - required: true - }, - type: { - type: String, - required: true - }, - setting: { - type: String, - required: true +const opts = { + toJSON: { + virtuals: true, + transform: function (doc: any, ret: any, options: any) { + delete ret._id + delete ret.id + return ret + } + } +} + +const permissionSchema = new Schema( + { + 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' } }, - user: { type: Schema.Types.ObjectId, ref: 'User' }, - group: { type: Schema.Types.ObjectId, ref: 'Group' } -}) + opts +) -// Hooks -permissionSchema.pre('save', async function () { - if (this.isNew) { - this.permissionId = await getSequenceNextValue('permissionId') - } +permissionSchema.virtual('uid').get(function () { + return this._id.toString() }) // Static Methods @@ -55,20 +63,14 @@ permissionSchema.static('get', async function (getBy: GetPermissionBy): Promise< PermissionDetailsResponse[] > { return (await this.find(getBy) - .select({ - _id: 0, - permissionId: 1, - path: 1, - type: 1, - setting: 1 - }) - .populate({ path: 'user', select: 'id username displayName isAdmin -_id' }) + .select('uid path type setting') + .populate({ path: 'user', select: 'uid username displayName isAdmin' }) .populate({ path: 'group', - select: 'groupId name description -_id', + select: 'uid name description', populate: { path: 'users', - select: 'id username displayName isAdmin -_id', + select: 'uid username displayName isAdmin', options: { limit: 15 } } })) as unknown as PermissionDetailsResponse[] diff --git a/api/src/model/User.ts b/api/src/model/User.ts index d0537924..74830ce0 100644 --- a/api/src/model/User.ts +++ b/api/src/model/User.ts @@ -1,6 +1,6 @@ -import { Schema, model, Document, Model } from 'mongoose' +import { Schema, model, Document, Model, ObjectId } from 'mongoose' import bcrypt from 'bcryptjs' -import { AuthProviderType, getSequenceNextValue } from '../utils' +import { AuthProviderType } from '../utils' export interface UserPayload { /** @@ -36,7 +36,6 @@ export interface UserPayload { interface IUserDocument extends UserPayload, Document { _id: Schema.Types.ObjectId - id: number isAdmin: boolean isActive: boolean needsToUpdatePassword: boolean @@ -44,6 +43,9 @@ interface IUserDocument extends UserPayload, Document { groups: Schema.Types.ObjectId[] tokens: [{ [key: string]: string }] authProvider?: AuthProviderType + + // Declare virtual properties as read-only properties + readonly uid: string } export interface IUser extends IUserDocument { @@ -54,70 +56,74 @@ export interface IUser extends IUserDocument { interface IUserModel extends Model { hashPassword(password: string): string } - -const userSchema = new Schema({ - displayName: { - type: String, - required: true - }, - username: { - type: String, - required: true, - unique: true - }, - id: { - type: Number, - unique: true - }, - password: { - type: String, - required: true - }, - authProvider: { - type: String, - enum: AuthProviderType - }, - isAdmin: { - type: Boolean, - default: false - }, - isActive: { - type: Boolean, - default: true - }, - needsToUpdatePassword: { - type: Boolean, - default: true - }, - autoExec: { - type: String - }, - groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }], - tokens: [ - { - clientId: { - type: String, - required: true - }, - accessToken: { - type: String, - required: true - }, - refreshToken: { - type: String, - required: true - } +const opts = { + toJSON: { + virtuals: true, + transform: function (doc: any, ret: any, options: any) { + delete ret._id + delete ret.id + return ret } - ] -}) - -// Hooks -userSchema.pre('save', async function (next) { - if (this.isNew) { - this.id = await getSequenceNextValue('id') } +} + +const userSchema = new Schema( + { + 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 +) - next() +userSchema.virtual('uid').get(function () { + return this._id.toString() }) // Static Methods diff --git a/api/src/routes/api/group.ts b/api/src/routes/api/group.ts index f8712ee1..7b2be9cc 100644 --- a/api/src/routes/api/group.ts +++ b/api/src/routes/api/group.ts @@ -1,7 +1,11 @@ import express from 'express' import { GroupController } from '../../controllers/' import { authenticateAccessToken, verifyAdmin } from '../../middlewares' -import { getGroupValidation, registerGroupValidation } from '../../utils' +import { + getGroupValidation, + registerGroupValidation, + uidValidation +} from '../../utils' const groupRouter = express.Router() @@ -33,12 +37,15 @@ groupRouter.get('/', authenticateAccessToken, async (req, res) => { } }) -groupRouter.get('/:groupId', authenticateAccessToken, async (req, res) => { - const { groupId } = req.params +groupRouter.get('/:uid', authenticateAccessToken, async (req, res) => { + 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() try { - const response = await controller.getGroup(parseInt(groupId)) + const response = await controller.getGroup(uid) res.send(response) } catch (err: any) { res.status(err.code).send(err.message) @@ -56,7 +63,7 @@ groupRouter.get( const controller = new GroupController() try { - const response = await controller.getGroupByGroupName(name) + const response = await controller.getGroupByName(name) res.send(response) } catch (err: any) { res.status(err.code).send(err.message) @@ -65,18 +72,15 @@ groupRouter.get( ) groupRouter.post( - '/:groupId/:userId', + '/:groupUid/:userUid', authenticateAccessToken, verifyAdmin, async (req, res) => { - const { groupId, userId } = req.params + const { groupUid, userUid } = req.params const controller = new GroupController() try { - const response = await controller.addUserToGroup( - parseInt(groupId), - parseInt(userId) - ) + const response = await controller.addUserToGroup(groupUid, userUid) res.send(response) } catch (err: any) { res.status(err.code).send(err.message) @@ -85,18 +89,15 @@ groupRouter.post( ) groupRouter.delete( - '/:groupId/:userId', + '/:groupUid/:userUid', authenticateAccessToken, verifyAdmin, async (req, res) => { - const { groupId, userId } = req.params + const { groupUid, userUid } = req.params const controller = new GroupController() try { - const response = await controller.removeUserFromGroup( - parseInt(groupId), - parseInt(userId) - ) + const response = await controller.removeUserFromGroup(groupUid, userUid) res.send(response) } catch (err: any) { res.status(err.code).send(err.message) @@ -105,15 +106,18 @@ groupRouter.delete( ) groupRouter.delete( - '/:groupId', + '/:uid', authenticateAccessToken, verifyAdmin, 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() try { - await controller.deleteGroup(parseInt(groupId)) + await controller.deleteGroup(uid) res.status(200).send('Group Deleted!') } catch (err: any) { res.status(err.code).send(err.message) diff --git a/api/src/routes/api/permission.ts b/api/src/routes/api/permission.ts index 93951243..c5d53900 100644 --- a/api/src/routes/api/permission.ts +++ b/api/src/routes/api/permission.ts @@ -3,6 +3,7 @@ import { PermissionController } from '../../controllers/' import { verifyAdmin } from '../../middlewares' import { registerPermissionValidation, + uidValidation, updatePermissionValidation } from '../../utils' @@ -34,14 +35,17 @@ permissionRouter.post('/', verifyAdmin, async (req, res) => { } }) -permissionRouter.patch('/:permissionId', verifyAdmin, async (req: any, res) => { - const { permissionId } = req.params +permissionRouter.patch('/:uid', verifyAdmin, async (req: any, res) => { + 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) if (error) return res.status(400).send(error.details[0].message) try { - const response = await controller.updatePermission(permissionId, body) + const response = await controller.updatePermission(uid, body) res.send(response) } catch (err: any) { const statusCode = err.code @@ -50,20 +54,18 @@ permissionRouter.patch('/:permissionId', verifyAdmin, async (req: any, res) => { } }) -permissionRouter.delete( - '/:permissionId', - verifyAdmin, - async (req: any, res) => { - const { permissionId } = req.params +permissionRouter.delete('/:uid', verifyAdmin, async (req: any, res) => { + const { error: uidError, value: params } = uidValidation(req.params) + if (uidError) return res.status(400).send(uidError.details[0].message) - try { - await controller.deletePermission(permissionId) - res.status(200).send('Permission Deleted!') - } catch (err: any) { - const statusCode = err.code - delete err.code - res.status(statusCode).send(err.message) - } + const { uid } = params + try { + await controller.deletePermission(uid) + res.status(200).send('Permission Deleted!') + } catch (err: any) { + const statusCode = err.code + delete err.code + res.status(statusCode).send(err.message) } -) +}) export default permissionRouter diff --git a/api/src/routes/api/spec/auth.spec.ts b/api/src/routes/api/spec/auth.spec.ts index 0e1e5b11..bc9288cc 100644 --- a/api/src/routes/api/spec/auth.spec.ts +++ b/api/src/routes/api/spec/auth.spec.ts @@ -13,6 +13,7 @@ import { generateAccessToken, generateAuthCode, generateRefreshToken, + randomBytesHexString, saveTokensInDB, verifyTokenInDB } from '../../../utils' @@ -20,7 +21,6 @@ import { const clientId = 'someclientID' const clientSecret = 'someclientSecret' const user = { - id: 1234, displayName: 'Test User', username: 'testUsername', password: '87654321', @@ -52,7 +52,7 @@ describe('auth', () => { describe('token', () => { const userInfo: InfoJWT = { clientId, - userId: user.id + userId: randomBytesHexString(12) } beforeAll(async () => { await userController.createUser(user) @@ -151,10 +151,10 @@ describe('auth', () => { currentUser = await userController.createUser(user) refreshToken = generateRefreshToken({ clientId, - userId: currentUser.id + userId: currentUser.uid }) await saveTokensInDB( - currentUser.id, + currentUser.uid, clientId, 'accessToken', refreshToken @@ -202,11 +202,11 @@ describe('auth', () => { currentUser = await userController.createUser(user) accessToken = generateAccessToken({ clientId, - userId: currentUser.id + userId: currentUser.uid }) await saveTokensInDB( - currentUser.id, + currentUser.uid, clientId, accessToken, 'refreshToken' diff --git a/api/src/routes/api/spec/client.spec.ts b/api/src/routes/api/spec/client.spec.ts index a901a65c..353b4d0d 100644 --- a/api/src/routes/api/spec/client.spec.ts +++ b/api/src/routes/api/spec/client.spec.ts @@ -40,10 +40,10 @@ describe('client', () => { const dbUser = await userController.createUser(adminUser) adminAccessToken = generateAccessToken({ clientId: client.clientId, - userId: dbUser.id + userId: dbUser.uid }) await saveTokensInDB( - dbUser.id, + dbUser.uid, client.clientId, adminAccessToken, 'refreshToken' @@ -95,10 +95,10 @@ describe('client', () => { const dbUser = await userController.createUser(user) const accessToken = generateAccessToken({ clientId: client.clientId, - userId: dbUser.id + userId: dbUser.uid }) await saveTokensInDB( - dbUser.id, + dbUser.uid, client.clientId, accessToken, 'refreshToken' @@ -212,10 +212,10 @@ describe('client', () => { const dbUser = await userController.createUser(user) const accessToken = generateAccessToken({ clientId: client.clientId, - userId: dbUser.id + userId: dbUser.uid }) await saveTokensInDB( - dbUser.id, + dbUser.uid, client.clientId, accessToken, 'refreshToken' diff --git a/api/src/routes/api/spec/drive.spec.ts b/api/src/routes/api/spec/drive.spec.ts index c4c28ce4..5e5bd5db 100644 --- a/api/src/routes/api/spec/drive.spec.ts +++ b/api/src/routes/api/spec/drive.spec.ts @@ -71,31 +71,31 @@ describe('drive', () => { con = await mongoose.connect(mongoServer.getUri()) const dbUser = await controller.createUser(user) - accessToken = await generateAndSaveToken(dbUser.id) + accessToken = await generateAndSaveToken(dbUser.uid) await permissionController.createPermission({ ...permission, path: '/SASjsApi/drive/deploy', - principalId: dbUser.id + principalId: dbUser.uid }) await permissionController.createPermission({ ...permission, path: '/SASjsApi/drive/deploy/upload', - principalId: dbUser.id + principalId: dbUser.uid }) await permissionController.createPermission({ ...permission, path: '/SASjsApi/drive/file', - principalId: dbUser.id + principalId: dbUser.uid }) await permissionController.createPermission({ ...permission, path: '/SASjsApi/drive/folder', - principalId: dbUser.id + principalId: dbUser.uid }) await permissionController.createPermission({ ...permission, 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) .members[0] as ServiceMember -const generateAndSaveToken = async (userId: number) => { +const generateAndSaveToken = async (userId: string) => { const adminAccessToken = generateAccessToken({ clientId, userId diff --git a/api/src/routes/api/spec/group.spec.ts b/api/src/routes/api/spec/group.spec.ts index 56af21aa..711c31cd 100644 --- a/api/src/routes/api/spec/group.spec.ts +++ b/api/src/routes/api/spec/group.spec.ts @@ -11,6 +11,7 @@ import { } from '../../../utils' import Group, { PUBLIC_GROUP_NAME } from '../../../model/Group' import User from '../../../model/User' +import { randomBytes } from 'crypto' const clientId = 'someclientID' const adminUser = { @@ -75,7 +76,7 @@ describe('group', () => { .send(group) .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) @@ -155,7 +156,7 @@ describe('group', () => { const dbGroup = await groupController.createGroup(group) const res = await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}`) + .delete(`/SASjsApi/group/${dbGroup.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) @@ -174,17 +175,17 @@ describe('group', () => { username: 'deletegroup2' }) - await groupController.addUserToGroup(dbGroup.groupId, dbUser1.id) - await groupController.addUserToGroup(dbGroup.groupId, dbUser2.id) + await groupController.addUserToGroup(dbGroup.uid, dbUser1.uid) + await groupController.addUserToGroup(dbGroup.uid, dbUser2.uid) await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}`) + .delete(`/SASjsApi/group/${dbGroup.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) const res1 = await request(app) - .get(`/SASjsApi/user/${dbUser1.id}`) + .get(`/SASjsApi/user/${dbUser1.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) @@ -192,7 +193,7 @@ describe('group', () => { expect(res1.body.groups).toEqual([]) const res2 = await request(app) - .get(`/SASjsApi/user/${dbUser2.id}`) + .get(`/SASjsApi/user/${dbUser2.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) @@ -201,8 +202,10 @@ describe('group', () => { }) it('should respond with Not Found if groupId is incorrect', async () => { + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .delete(`/SASjsApi/group/1234`) + .delete(`/SASjsApi/group/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -229,7 +232,7 @@ describe('group', () => { }) const res = await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}`) + .delete(`/SASjsApi/group/${dbGroup.uid}`) .auth(accessToken, { type: 'bearer' }) .send() .expect(401) @@ -245,15 +248,15 @@ describe('group', () => { }) it('should respond with group', async () => { - const { groupId } = await groupController.createGroup(group) + const { uid } = await groupController.createGroup(group) const res = await request(app) - .get(`/SASjsApi/group/${groupId}`) + .get(`/SASjsApi/group/${uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) @@ -266,15 +269,15 @@ describe('group', () => { username: 'get' + user.username }) - const { groupId } = await groupController.createGroup(group) + const { uid } = await groupController.createGroup(group) const res = await request(app) - .get(`/SASjsApi/group/${groupId}`) + .get(`/SASjsApi/group/${uid}`) .auth(accessToken, { type: 'bearer' }) .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) @@ -292,8 +295,10 @@ describe('group', () => { }) it('should respond with Not Found if groupId is incorrect', async () => { + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .get('/SASjsApi/group/1234') + .get(`/SASjsApi/group/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -312,7 +317,7 @@ describe('group', () => { .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) @@ -333,7 +338,7 @@ describe('group', () => { .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) @@ -379,7 +384,7 @@ describe('group', () => { expect(res.body).toEqual([ { - groupId: expect.anything(), + uid: expect.anything(), name: group.name, description: group.description } @@ -401,7 +406,7 @@ describe('group', () => { expect(res.body).toEqual([ { - groupId: expect.anything(), + uid: expect.anything(), name: group.name, description: group.description } @@ -426,18 +431,18 @@ describe('group', () => { const dbUser = await userController.createUser(user) const res = await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) expect(res.body.users).toEqual([ { - id: expect.anything(), + uid: expect.anything(), username: user.username, displayName: user.displayName } @@ -452,20 +457,20 @@ describe('group', () => { }) await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) const res = await request(app) - .get(`/SASjsApi/user/${dbUser.id}`) + .get(`/SASjsApi/user/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) expect(res.body.groups).toEqual([ { - groupId: expect.anything(), + uid: expect.anything(), name: group.name, description: group.description } @@ -478,21 +483,21 @@ describe('group', () => { ...user, username: 'addUserRandomUser' }) - await groupController.addUserToGroup(dbGroup.groupId, dbUser.id) + await groupController.addUserToGroup(dbGroup.uid, dbUser.uid) const res = await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) expect(res.body.users).toEqual([ { - id: expect.anything(), + uid: expect.anything(), username: 'addUserRandomUser', displayName: user.displayName } @@ -526,8 +531,10 @@ describe('group', () => { }) it('should respond with Not Found if groupId is incorrect', async () => { + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .post('/SASjsApi/group/123/123') + .post(`/SASjsApi/group/${hexValue}/123`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -538,8 +545,10 @@ describe('group', () => { it('should respond with Not Found if userId is incorrect', async () => { const dbGroup = await groupController.createGroup(group) + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/123`) + .post(`/SASjsApi/group/${dbGroup.uid}/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -556,7 +565,7 @@ describe('group', () => { }) const res = await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(400) @@ -577,7 +586,7 @@ describe('group', () => { }) const res = await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(405) @@ -596,7 +605,7 @@ describe('group', () => { }) const res = await request(app) - .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .post(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(405) @@ -618,15 +627,15 @@ describe('group', () => { ...user, username: 'removeUserRandomUser' }) - await groupController.addUserToGroup(dbGroup.groupId, dbUser.id) + await groupController.addUserToGroup(dbGroup.uid, dbUser.uid) const res = await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) - expect(res.body.groupId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.name).toEqual(group.name) expect(res.body.description).toEqual(group.description) expect(res.body.isActive).toEqual(true) @@ -639,16 +648,16 @@ describe('group', () => { ...user, username: 'removeGroupFromUser' }) - await groupController.addUserToGroup(dbGroup.groupId, dbUser.id) + await groupController.addUserToGroup(dbGroup.uid, dbUser.uid) await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) const res = await request(app) - .get(`/SASjsApi/user/${dbUser.id}`) + .get(`/SASjsApi/user/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) @@ -667,7 +676,7 @@ describe('group', () => { }) const res = await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(405) @@ -686,7 +695,7 @@ describe('group', () => { }) const res = await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .delete(`/SASjsApi/group/${dbGroup.uid}/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(405) @@ -723,8 +732,10 @@ describe('group', () => { }) it('should respond with Not Found if groupId is incorrect', async () => { + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .delete('/SASjsApi/group/123/123') + .delete(`/SASjsApi/group/${hexValue}/123`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -735,8 +746,10 @@ describe('group', () => { it('should respond with Not Found if userId is incorrect', async () => { const dbGroup = await groupController.createGroup(group) + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .delete(`/SASjsApi/group/${dbGroup.groupId}/123`) + .delete(`/SASjsApi/group/${dbGroup.uid}/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -752,10 +765,10 @@ const generateSaveTokenAndCreateUser = async ( ): Promise => { 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({ clientId, userId diff --git a/api/src/routes/api/spec/permission.spec.ts b/api/src/routes/api/spec/permission.spec.ts index f193d05f..ccdd120e 100644 --- a/api/src/routes/api/spec/permission.spec.ts +++ b/api/src/routes/api/spec/permission.spec.ts @@ -17,6 +17,7 @@ import { PermissionDetailsResponse } from '../../../controllers' import { generateAccessToken, saveTokensInDB } from '../../../utils' +import { randomBytes } from 'crypto' const deployPayload = { appLoc: 'string', @@ -103,10 +104,10 @@ describe('permission', () => { const res = await request(app) .post('/SASjsApi/permission') .auth(adminAccessToken, { type: 'bearer' }) - .send({ ...permission, principalId: dbUser.id }) + .send({ ...permission, principalId: dbUser.uid }) .expect(200) - expect(res.body.permissionId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.path).toEqual(permission.path) expect(res.body.type).toEqual(permission.type) expect(res.body.setting).toEqual(permission.setting) @@ -122,11 +123,11 @@ describe('permission', () => { .send({ ...permission, principalType: 'group', - principalId: dbGroup.groupId + principalId: dbGroup.uid }) .expect(200) - expect(res.body.permissionId).toBeTruthy() + expect(res.body.uid).toBeTruthy() expect(res.body.path).toEqual(permission.path) expect(res.body.type).toEqual(permission.type) 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 () => { - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) .post('/SASjsApi/permission') @@ -281,17 +282,19 @@ describe('permission', () => { 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) .post('/SASjsApi/permission') .auth(adminAccessToken, { type: 'bearer' }) .send({ ...permission, - principalId: 'someCharacters' + principalId: randomBytes(10).toString('hex') }) .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({}) }) @@ -307,7 +310,7 @@ describe('permission', () => { .auth(adminAccessToken, { type: 'bearer' }) .send({ ...permission, - principalId: adminUser.id + principalId: adminUser.uid }) .expect(400) @@ -321,7 +324,7 @@ describe('permission', () => { .auth(adminAccessToken, { type: 'bearer' }) .send({ ...permission, - principalId: 123 + principalId: randomBytes(12).toString('hex') }) .expect(404) @@ -336,7 +339,7 @@ describe('permission', () => { .send({ ...permission, principalType: 'group', - principalId: 123 + principalId: randomBytes(12).toString('hex') }) .expect(404) @@ -347,13 +350,13 @@ describe('permission', () => { it('should respond with Conflict (409) if permission already exists', async () => { await permissionController.createPermission({ ...permission, - principalId: dbUser.id + principalId: dbUser.uid }) const res = await request(app) .post('/SASjsApi/permission') .auth(adminAccessToken, { type: 'bearer' }) - .send({ ...permission, principalId: dbUser.id }) + .send({ ...permission, principalId: dbUser.uid }) .expect(409) expect(res.text).toEqual( @@ -368,7 +371,7 @@ describe('permission', () => { beforeAll(async () => { dbPermission = await permissionController.createPermission({ ...permission, - principalId: dbUser.id + principalId: dbUser.uid }) }) @@ -378,7 +381,7 @@ describe('permission', () => { it('should respond with updated permission', async () => { const res = await request(app) - .patch(`/SASjsApi/permission/${dbPermission?.permissionId}`) + .patch(`/SASjsApi/permission/${dbPermission?.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send({ setting: PermissionSettingForRoute.deny }) .expect(200) @@ -388,7 +391,7 @@ describe('permission', () => { it('should respond with Unauthorized if access token is not present', async () => { const res = await request(app) - .patch(`/SASjsApi/permission/${dbPermission?.permissionId}`) + .patch(`/SASjsApi/permission/${dbPermission?.uid}`) .send() .expect(401) @@ -403,7 +406,7 @@ describe('permission', () => { }) const res = await request(app) - .patch(`/SASjsApi/permission/${dbPermission?.permissionId}`) + .patch(`/SASjsApi/permission/${dbPermission?.uid}`) .auth(accessToken, { type: 'bearer' }) .send() .expect(401) @@ -414,7 +417,7 @@ describe('permission', () => { it('should respond with Bad Request if setting is missing', async () => { const res = await request(app) - .patch(`/SASjsApi/permission/${dbPermission?.permissionId}`) + .patch(`/SASjsApi/permission/${dbPermission?.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(400) @@ -425,7 +428,7 @@ describe('permission', () => { it('should respond with Bad Request if setting is invalid', async () => { const res = await request(app) - .patch(`/SASjsApi/permission/${dbPermission?.permissionId}`) + .patch(`/SASjsApi/permission/${dbPermission?.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send({ setting: 'invalid' @@ -437,8 +440,9 @@ describe('permission', () => { }) 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) - .patch('/SASjsApi/permission/123') + .patch(`/SASjsApi/permission/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send({ setting: PermissionSettingForRoute.deny @@ -454,10 +458,10 @@ describe('permission', () => { it('should delete permission', async () => { const dbPermission = await permissionController.createPermission({ ...permission, - principalId: dbUser.id + principalId: dbUser.uid }) const res = await request(app) - .delete(`/SASjsApi/permission/${dbPermission?.permissionId}`) + .delete(`/SASjsApi/permission/${dbPermission?.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) @@ -466,8 +470,10 @@ describe('permission', () => { }) 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) - .delete('/SASjsApi/permission/123') + .delete(`/SASjsApi/permission/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -481,12 +487,12 @@ describe('permission', () => { await permissionController.createPermission({ ...permission, path: '/test-1', - principalId: dbUser.id + principalId: dbUser.uid }) await permissionController.createPermission({ ...permission, path: '/test-2', - principalId: dbUser.id + principalId: dbUser.uid }) }) @@ -505,12 +511,12 @@ describe('permission', () => { ...user, username: 'get' + user.username }) - const accessToken = await generateAndSaveToken(nonAdminUser.id) + const accessToken = await generateAndSaveToken(nonAdminUser.uid) await permissionController.createPermission({ path: '/test-1', type: PermissionType.route, principalType: PrincipalType.user, - principalId: nonAdminUser.id, + principalId: nonAdminUser.uid, setting: PermissionSettingForRoute.grant }) @@ -531,7 +537,7 @@ describe('permission', () => { await permissionController.createPermission({ ...permission, path: '/SASjsApi/drive/deploy', - principalId: dbUser.id + principalId: dbUser.uid }) }) @@ -551,7 +557,7 @@ describe('permission', () => { }) it('should create files in SASJS drive', async () => { - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) await request(app) .get('/SASjsApi/drive/deploy') @@ -561,7 +567,7 @@ describe('permission', () => { }) it('should respond unauthorized', async () => { - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) await request(app) .get('/SASjsApi/drive/deploy/upload') @@ -577,10 +583,10 @@ const generateSaveTokenAndCreateUser = async ( ): Promise => { 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({ clientId, userId diff --git a/api/src/routes/api/spec/stp.spec.ts b/api/src/routes/api/spec/stp.spec.ts index 1512378b..f21c97cc 100644 --- a/api/src/routes/api/spec/stp.spec.ts +++ b/api/src/routes/api/spec/stp.spec.ts @@ -58,12 +58,12 @@ describe('stp', () => { mongoServer = await MongoMemoryServer.create() con = await mongoose.connect(mongoServer.getUri()) const dbUser = await userController.createUser(user) - accessToken = await generateAndSaveToken(dbUser.id) + accessToken = await generateAndSaveToken(dbUser.uid) await permissionController.createPermission({ path: '/SASjsApi/stp/execute', type: PermissionType.route, principalType: PrincipalType.user, - principalId: dbUser.id, + principalId: dbUser.uid, setting: PermissionSettingForRoute.grant }) }) @@ -456,7 +456,7 @@ const makeRequestAndAssert = async ( ) } -const generateAndSaveToken = async (userId: number) => { +const generateAndSaveToken = async (userId: string) => { const accessToken = generateAccessToken({ clientId, userId diff --git a/api/src/routes/api/spec/user.spec.ts b/api/src/routes/api/spec/user.spec.ts index da8b8295..71ceb881 100644 --- a/api/src/routes/api/spec/user.spec.ts +++ b/api/src/routes/api/spec/user.spec.ts @@ -1,3 +1,4 @@ +import { randomBytes } from 'crypto' import { Express } from 'express' import mongoose, { Mongoose } from 'mongoose' import { MongoMemoryServer } from 'mongodb-memory-server' @@ -101,9 +102,9 @@ describe('user', () => { const dbUser = await controller.createUser(user) const accessToken = generateAccessToken({ 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) .post('/SASjsApi/user') @@ -187,7 +188,7 @@ describe('user', () => { const newDisplayName = 'My new display Name' const res = await request(app) - .patch(`/SASjsApi/user/${dbUser.id}`) + .patch(`/SASjsApi/user/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send({ ...user, displayName: newDisplayName }) .expect(200) @@ -200,11 +201,11 @@ describe('user', () => { it('should respond with updated user when user himself requests', async () => { 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 res = await request(app) - .patch(`/SASjsApi/user/${dbUser.id}`) + .patch(`/SASjsApi/user/${dbUser.uid}`) .auth(accessToken, { type: 'bearer' }) .send({ displayName: newDisplayName, @@ -221,11 +222,11 @@ describe('user', () => { it('should respond with Bad Request, only admin can update isAdmin/isActive', async () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const newDisplayName = 'My new display Name' await request(app) - .patch(`/SASjsApi/user/${dbUser.id}`) + .patch(`/SASjsApi/user/${dbUser.uid}`) .auth(accessToken, { type: 'bearer' }) .send({ ...user, displayName: newDisplayName }) .expect(400) @@ -277,10 +278,10 @@ describe('user', () => { ...user, username: 'randomUser' }) - const accessToken = await generateAndSaveToken(dbUser2.id) + const accessToken = await generateAndSaveToken(dbUser2.uid) const res = await request(app) - .patch(`/SASjsApi/user/${dbUser1.id}`) + .patch(`/SASjsApi/user/${dbUser1.uid}`) .auth(accessToken, { type: 'bearer' }) .send(user) .expect(401) @@ -297,7 +298,7 @@ describe('user', () => { }) const res = await request(app) - .patch(`/SASjsApi/user/${dbUser1.id}`) + .patch(`/SASjsApi/user/${dbUser1.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send({ username: dbUser2.username }) .expect(409) @@ -325,7 +326,7 @@ describe('user', () => { it('should respond with updated user when user himself requests', async () => { 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 res = await request(app) @@ -346,7 +347,7 @@ describe('user', () => { it('should respond with Bad Request, only admin can update isAdmin/isActive', async () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const newDisplayName = 'My new display Name' await request(app) @@ -372,10 +373,10 @@ describe('user', () => { ...user, username: 'randomUser' }) - const accessToken = await generateAndSaveToken(dbUser2.id) + const accessToken = await generateAndSaveToken(dbUser2.uid) const res = await request(app) - .patch(`/SASjsApi/user/${dbUser1.id}`) + .patch(`/SASjsApi/user/${dbUser1.uid}`) .auth(accessToken, { type: 'bearer' }) .send(user) .expect(401) @@ -418,7 +419,7 @@ describe('user', () => { const dbUser = await controller.createUser(user) const res = await request(app) - .delete(`/SASjsApi/user/${dbUser.id}`) + .delete(`/SASjsApi/user/${dbUser.uid}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(200) @@ -428,10 +429,10 @@ describe('user', () => { it('should respond with OK when user himself requests', async () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) - .delete(`/SASjsApi/user/${dbUser.id}`) + .delete(`/SASjsApi/user/${dbUser.uid}`) .auth(accessToken, { type: 'bearer' }) .send({ password: user.password }) .expect(200) @@ -441,10 +442,10 @@ describe('user', () => { it('should respond with Bad Request when user himself requests and password is missing', async () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) - .delete(`/SASjsApi/user/${dbUser.id}`) + .delete(`/SASjsApi/user/${dbUser.uid}`) .auth(accessToken, { type: 'bearer' }) .send() .expect(400) @@ -469,10 +470,10 @@ describe('user', () => { ...user, username: 'randomUser' }) - const accessToken = await generateAndSaveToken(dbUser2.id) + const accessToken = await generateAndSaveToken(dbUser2.uid) const res = await request(app) - .delete(`/SASjsApi/user/${dbUser1.id}`) + .delete(`/SASjsApi/user/${dbUser1.uid}`) .auth(accessToken, { type: 'bearer' }) .send(user) .expect(401) @@ -483,10 +484,10 @@ describe('user', () => { it('should respond with Unauthorized when user himself requests and password is incorrect', async () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) - .delete(`/SASjsApi/user/${dbUser.id}`) + .delete(`/SASjsApi/user/${dbUser.uid}`) .auth(accessToken, { type: 'bearer' }) .send({ password: 'incorrectpassword' }) .expect(401) @@ -510,7 +511,7 @@ describe('user', () => { it('should respond with OK when user himself requests', async () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) .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 () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) .delete(`/SASjsApi/user/by/username/${dbUser.username}`) @@ -551,7 +552,7 @@ describe('user', () => { ...user, username: 'randomUser' }) - const accessToken = await generateAndSaveToken(dbUser2.id) + const accessToken = await generateAndSaveToken(dbUser2.uid) const res = await request(app) .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 () => { const dbUser = await controller.createUser(user) - const accessToken = await generateAndSaveToken(dbUser.id) + const accessToken = await generateAndSaveToken(dbUser.uid) const res = await request(app) .delete(`/SASjsApi/user/by/username/${dbUser.username}`) @@ -592,7 +593,7 @@ describe('user', () => { it('should respond with user autoExec when same user requests', async () => { const dbUser = await controller.createUser(user) - const userId = dbUser.id + const userId = dbUser.uid const accessToken = await generateAndSaveToken(userId) const res = await request(app) @@ -611,7 +612,7 @@ describe('user', () => { it('should respond with user autoExec when admin user requests', async () => { const dbUser = await controller.createUser(user) - const userId = dbUser.id + const userId = dbUser.uid const res = await request(app) .get(`/SASjsApi/user/${userId}`) @@ -634,7 +635,7 @@ describe('user', () => { }) const dbUser = await controller.createUser(user) - const userId = dbUser.id + const userId = dbUser.uid const res = await request(app) .get(`/SASjsApi/user/${userId}`) @@ -652,7 +653,7 @@ describe('user', () => { it('should respond with user along with associated groups', async () => { const dbUser = await controller.createUser(user) - const userId = dbUser.id + const userId = dbUser.uid const accessToken = await generateAndSaveToken(userId) const group = { @@ -661,7 +662,7 @@ describe('user', () => { } const groupController = new GroupController() 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) .get(`/SASjsApi/user/${userId}`) @@ -690,8 +691,10 @@ describe('user', () => { it('should respond with Not Found if userId is incorrect', async () => { await controller.createUser(user) + const hexValue = randomBytes(12).toString('hex') + const res = await request(app) - .get('/SASjsApi/user/1234') + .get(`/SASjsApi/user/${hexValue}`) .auth(adminAccessToken, { type: 'bearer' }) .send() .expect(404) @@ -703,7 +706,7 @@ describe('user', () => { describe('by username', () => { it('should respond with user autoExec when same user requests', async () => { const dbUser = await controller.createUser(user) - const userId = dbUser.id + const userId = dbUser.uid const accessToken = await generateAndSaveToken(userId) const res = await request(app) @@ -803,13 +806,13 @@ describe('user', () => { expect(res.body).toEqual([ { - id: expect.anything(), + uid: expect.anything(), username: adminUser.username, displayName: adminUser.displayName, isAdmin: adminUser.isAdmin }, { - id: expect.anything(), + uid: expect.anything(), username: user.username, displayName: user.displayName, isAdmin: user.isAdmin @@ -831,13 +834,13 @@ describe('user', () => { expect(res.body).toEqual([ { - id: expect.anything(), + uid: expect.anything(), username: adminUser.username, displayName: adminUser.displayName, isAdmin: adminUser.isAdmin }, { - id: expect.anything(), + uid: expect.anything(), username: 'randomUser', displayName: user.displayName, isAdmin: user.isAdmin @@ -859,10 +862,10 @@ const generateSaveTokenAndCreateUser = async ( ): Promise => { 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({ clientId, userId diff --git a/api/src/routes/api/spec/web.spec.ts b/api/src/routes/api/spec/web.spec.ts index f5529aac..17480887 100644 --- a/api/src/routes/api/spec/web.spec.ts +++ b/api/src/routes/api/spec/web.spec.ts @@ -145,7 +145,7 @@ describe('web', () => { expect(res.body.loggedIn).toBeTruthy() expect(res.body.user).toEqual({ - id: expect.any(Number), + id: expect.any(String), username: user.username, displayName: user.displayName, isAdmin: user.isAdmin, diff --git a/api/src/routes/api/user.ts b/api/src/routes/api/user.ts index d0ee7cc5..987e0276 100644 --- a/api/src/routes/api/user.ts +++ b/api/src/routes/api/user.ts @@ -9,6 +9,7 @@ import { deleteUserValidation, getUserValidation, registerUserValidation, + uidValidation, updateUserValidation } from '../../utils' @@ -56,12 +57,15 @@ userRouter.get( } ) -userRouter.get('/:userId', authenticateAccessToken, async (req, res) => { - const { userId } = req.params +userRouter.get('/:uid', authenticateAccessToken, async (req, res) => { + 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() try { - const response = await controller.getUser(req, parseInt(userId)) + const response = await controller.getUser(req, uid) res.send(response) } catch (err: any) { res.status(err.code).send(err.message) @@ -97,12 +101,16 @@ userRouter.patch( ) userRouter.patch( - '/:userId', + '/:uid', authenticateAccessToken, verifyAdminIfNeeded, async (req, res) => { 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 const { error, value: body } = updateUserValidation(req.body, user!.isAdmin) @@ -110,7 +118,7 @@ userRouter.patch( const controller = new UserController() try { - const response = await controller.updateUser(parseInt(userId), body) + const response = await controller.updateUser(uid, body) res.send(response) } catch (err: any) { res.status(err.code).send(err.message) @@ -147,12 +155,16 @@ userRouter.delete( ) userRouter.delete( - '/:userId', + '/:uid', authenticateAccessToken, verifyAdminIfNeeded, async (req, res) => { 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 const { error, value: data } = deleteUserValidation(req.body, user!.isAdmin) @@ -160,7 +172,7 @@ userRouter.delete( const controller = new UserController() try { - await controller.deleteUser(parseInt(userId), data, user!.isAdmin) + await controller.deleteUser(uid, data, user!.isAdmin) res.status(200).send('Account Deleted!') } catch (err: any) { res.status(err.code).send(err.message) diff --git a/api/src/types/InfoJWT.ts b/api/src/types/InfoJWT.ts index 07b7e273..98238127 100644 --- a/api/src/types/InfoJWT.ts +++ b/api/src/types/InfoJWT.ts @@ -1,4 +1,4 @@ export interface InfoJWT { clientId: string - userId: number + userId: string } diff --git a/api/src/types/PreProgramVars.ts b/api/src/types/PreProgramVars.ts index 1304aa97..29e07065 100644 --- a/api/src/types/PreProgramVars.ts +++ b/api/src/types/PreProgramVars.ts @@ -1,6 +1,6 @@ export interface PreProgramVars { username: string - userId: number + userId: string displayName: string serverUrl: string httpHeaders: string[] diff --git a/api/src/types/RequestUser.ts b/api/src/types/RequestUser.ts index fe4cdab0..70e41b1a 100644 --- a/api/src/types/RequestUser.ts +++ b/api/src/types/RequestUser.ts @@ -1,5 +1,5 @@ export interface RequestUser { - userId: number + userId: string clientId: string username: string displayName: string diff --git a/api/src/utils/crypto.ts b/api/src/utils/crypto.ts new file mode 100644 index 00000000..d9d4b38c --- /dev/null +++ b/api/src/utils/crypto.ts @@ -0,0 +1,4 @@ +import { randomBytes } from 'crypto' + +export const randomBytesHexString = (bytesCount: number) => + randomBytes(bytesCount).toString('hex') diff --git a/api/src/utils/getPreProgramVariables.ts b/api/src/utils/getPreProgramVariables.ts index 5e2d26a6..7c2f2a13 100644 --- a/api/src/utils/getPreProgramVariables.ts +++ b/api/src/utils/getPreProgramVariables.ts @@ -22,7 +22,7 @@ export const getPreProgramVariables = (req: Request): PreProgramVars => { //So this is workaround. return { username: user ? user.username : 'demo', - userId: user ? user.userId : 0, + userId: user ? user.userId : 'demoId', displayName: user ? user.displayName : 'demo', serverUrl: protocol + host, httpHeaders diff --git a/api/src/utils/getSequenceNextValue.ts b/api/src/utils/getSequenceNextValue.ts deleted file mode 100644 index 7ce7abbf..00000000 --- a/api/src/utils/getSequenceNextValue.ts +++ /dev/null @@ -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 -} diff --git a/api/src/utils/getTokensFromDB.ts b/api/src/utils/getTokensFromDB.ts index a5270863..0f477dda 100644 --- a/api/src/utils/getTokensFromDB.ts +++ b/api/src/utils/getTokensFromDB.ts @@ -4,7 +4,7 @@ import User from '../model/User' const isValidToken = async ( token: string, key: string, - userId: number, + userId: string, clientId: string ) => { const promise = new Promise((resolve, reject) => @@ -22,8 +22,8 @@ const isValidToken = async ( return await promise.then(() => true).catch(() => false) } -export const getTokensFromDB = async (userId: number, clientId: string) => { - const user = await User.findOne({ id: userId }) +export const getTokensFromDB = async (userId: string, clientId: string) => { + const user = await User.findOne({ _id: userId }) if (!user) return const currentTokenObj = user.tokens.find( diff --git a/api/src/utils/index.ts b/api/src/utils/index.ts index ec211c52..cd8942f2 100644 --- a/api/src/utils/index.ts +++ b/api/src/utils/index.ts @@ -2,6 +2,7 @@ export * from './appStreamConfig' export * from './connectDB' export * from './copySASjsCore' export * from './createWeboutSasFile' +export * from './crypto' export * from './desktopAutoExec' export * from './extractHeaders' export * from './extractName' @@ -14,7 +15,6 @@ export * from './getCertificates' export * from './getDesktopFields' export * from './getPreProgramVariables' export * from './getRunTimeAndFilePath' -export * from './getSequenceNextValue' export * from './getServerUrl' export * from './getTokensFromDB' export * from './instantiateLogger' diff --git a/api/src/utils/isPublicRoute.ts b/api/src/utils/isPublicRoute.ts index 178721f3..78c01c64 100644 --- a/api/src/utils/isPublicRoute.ts +++ b/api/src/utils/isPublicRoute.ts @@ -22,7 +22,7 @@ export const isPublicRoute = async (req: Request): Promise => { } export const publicUser: RequestUser = { - userId: 0, + userId: 'public_user_id', clientId: 'public_app', username: 'publicUser', displayName: 'Public User', diff --git a/api/src/utils/removeTokensInDB.ts b/api/src/utils/removeTokensInDB.ts index 735fd009..5c859234 100644 --- a/api/src/utils/removeTokensInDB.ts +++ b/api/src/utils/removeTokensInDB.ts @@ -1,7 +1,7 @@ import User from '../model/User' -export const removeTokensInDB = async (userId: number, clientId: string) => { - const user = await User.findOne({ id: userId }) +export const removeTokensInDB = async (userId: string, clientId: string) => { + const user = await User.findOne({ _id: userId }) if (!user) return const tokenObjIndex = user.tokens.findIndex( diff --git a/api/src/utils/saveTokensInDB.ts b/api/src/utils/saveTokensInDB.ts index c89e39d8..1b8ceea4 100644 --- a/api/src/utils/saveTokensInDB.ts +++ b/api/src/utils/saveTokensInDB.ts @@ -1,12 +1,12 @@ import User from '../model/User' export const saveTokensInDB = async ( - userId: number, + userId: string, clientId: string, accessToken: string, refreshToken: string ) => { - const user = await User.findOne({ id: userId }) + const user = await User.findOne({ _id: userId }) if (!user) return const currentTokenObj = user.tokens.find( diff --git a/api/src/utils/seedDB.ts b/api/src/utils/seedDB.ts index 6a37b79b..a9fb60df 100644 --- a/api/src/utils/seedDB.ts +++ b/api/src/utils/seedDB.ts @@ -82,7 +82,7 @@ export const seedDB = async (): Promise => { } export const ALL_USERS_GROUP = { - name: 'AllUsers', + name: 'all-users', description: 'Group contains all users' } diff --git a/api/src/utils/validation.ts b/api/src/utils/validation.ts index feed9709..11f2634f 100644 --- a/api/src/utils/validation.ts +++ b/api/src/utils/validation.ts @@ -12,6 +12,11 @@ const groupnameSchema = Joi.string().lowercase().alphanum().min(3).max(16) 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 => Joi.object({ username: usernameSchema.required() @@ -113,7 +118,7 @@ export const registerPermissionValidation = (data: any): Joi.ValidationResult => principalType: Joi.string() .required() .valid(...Object.values(PrincipalType)), - principalId: Joi.number().required() + principalId: Joi.string().length(24).hex().required() }).validate(data) export const updatePermissionValidation = (data: any): Joi.ValidationResult => diff --git a/api/src/utils/verifyTokenInDB.ts b/api/src/utils/verifyTokenInDB.ts index 9ee12c65..d612db97 100644 --- a/api/src/utils/verifyTokenInDB.ts +++ b/api/src/utils/verifyTokenInDB.ts @@ -4,7 +4,7 @@ import { RequestUser } from '../types' export const fetchLatestAutoExec = async ( reqUser: RequestUser ): Promise => { - const dbUser = await User.findOne({ id: reqUser.userId }) + const dbUser = await User.findOne({ _id: reqUser.userId }) if (!dbUser) return undefined @@ -21,12 +21,12 @@ export const fetchLatestAutoExec = async ( } export const verifyTokenInDB = async ( - userId: number, + userId: string, clientId: string, token: string, tokenType: 'accessToken' | 'refreshToken' ): Promise => { - const dbUser = await User.findOne({ id: userId }) + const dbUser = await User.findOne({ _id: userId }) if (!dbUser) return undefined diff --git a/web/src/containers/Settings/internal/components/addPermissionModal.tsx b/web/src/containers/Settings/internal/components/addPermissionModal.tsx index 2996f4ee..1923317e 100644 --- a/web/src/containers/Settings/internal/components/addPermissionModal.tsx +++ b/web/src/containers/Settings/internal/components/addPermissionModal.tsx @@ -99,8 +99,8 @@ const AddPermissionModal = ({ principalType: principalType.toLowerCase(), principalId: principalType.toLowerCase() === 'user' - ? userPrincipal?.id - : groupPrincipal?.groupId + ? userPrincipal?.uid + : groupPrincipal?.uid } permissions.push(addPermissionPayload) diff --git a/web/src/containers/Settings/internal/components/permissionTable.tsx b/web/src/containers/Settings/internal/components/permissionTable.tsx index 550f72fb..12a51069 100644 --- a/web/src/containers/Settings/internal/components/permissionTable.tsx +++ b/web/src/containers/Settings/internal/components/permissionTable.tsx @@ -61,7 +61,7 @@ const PermissionTable = ({ {permissions.map((permission) => ( - + {permission.path} {permission.type} diff --git a/web/src/containers/Settings/internal/hooks/useAddPermission.tsx b/web/src/containers/Settings/internal/hooks/useAddPermission.tsx index c692badc..63526332 100644 --- a/web/src/containers/Settings/internal/hooks/useAddPermission.tsx +++ b/web/src/containers/Settings/internal/hooks/useAddPermission.tsx @@ -69,7 +69,7 @@ const useAddPermission = () => { for (const permission of updatingPermissions) { await axios - .patch(`/SASjsApi/permission/${permission.permissionId}`, { + .patch(`/SASjsApi/permission/${permission.uid}`, { setting: permission.setting === 'Grant' ? 'Deny' : 'Grant' }) .then((res) => { diff --git a/web/src/containers/Settings/internal/hooks/useDeletePermissionModal.tsx b/web/src/containers/Settings/internal/hooks/useDeletePermissionModal.tsx index 7638c22e..960cc9a0 100644 --- a/web/src/containers/Settings/internal/hooks/useDeletePermissionModal.tsx +++ b/web/src/containers/Settings/internal/hooks/useDeletePermissionModal.tsx @@ -24,7 +24,7 @@ const useDeletePermissionModal = () => { setDeleteConfirmationModalOpen(false) setIsLoading(true) axios - .delete(`/SASjsApi/permission/${selectedPermission?.permissionId}`) + .delete(`/SASjsApi/permission/${selectedPermission?.uid}`) .then((res: any) => { fetchPermissions() setSnackbarMessage('Permission deleted!') diff --git a/web/src/containers/Settings/internal/hooks/useFilterPermissions.tsx b/web/src/containers/Settings/internal/hooks/useFilterPermissions.tsx index 7ca1f6a5..6b079cd7 100644 --- a/web/src/containers/Settings/internal/hooks/useFilterPermissions.tsx +++ b/web/src/containers/Settings/internal/hooks/useFilterPermissions.tsx @@ -62,21 +62,17 @@ const useFilterPermissions = () => { : permissions let filteredArray = uriFilteredPermissions.filter((permission) => - principalFilteredPermissions.some( - (item) => item.permissionId === permission.permissionId - ) + principalFilteredPermissions.some((item) => item.uid === permission.uid) ) filteredArray = filteredArray.filter((permission) => principalTypeFilteredPermissions.some( - (item) => item.permissionId === permission.permissionId + (item) => item.uid === permission.uid ) ) filteredArray = filteredArray.filter((permission) => - settingFilteredPermissions.some( - (item) => item.permissionId === permission.permissionId - ) + settingFilteredPermissions.some((item) => item.uid === permission.uid) ) setFilteredPermissions(filteredArray) diff --git a/web/src/containers/Settings/internal/hooks/useUpdatePermissionModal.tsx b/web/src/containers/Settings/internal/hooks/useUpdatePermissionModal.tsx index 0deceee1..00258191 100644 --- a/web/src/containers/Settings/internal/hooks/useUpdatePermissionModal.tsx +++ b/web/src/containers/Settings/internal/hooks/useUpdatePermissionModal.tsx @@ -24,7 +24,7 @@ const useUpdatePermissionModal = () => { setUpdatePermissionModalOpen(false) setIsLoading(true) axios - .patch(`/SASjsApi/permission/${selectedPermission?.permissionId}`, { + .patch(`/SASjsApi/permission/${selectedPermission?.uid}`, { setting }) .then((res: any) => { diff --git a/web/src/containers/Settings/profile.tsx b/web/src/containers/Settings/profile.tsx index 5f366109..4d9bae19 100644 --- a/web/src/containers/Settings/profile.tsx +++ b/web/src/containers/Settings/profile.tsx @@ -26,18 +26,20 @@ const Profile = () => { const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false) useEffect(() => { - setIsLoading(true) - axios - .get(`/SASjsApi/user/${appContext.userId}`) - .then((res: any) => { - setUser(res.data) - }) - .catch((err) => { - console.log(err) - }) - .finally(() => { - setIsLoading(false) - }) + if (appContext.userId) { + setIsLoading(true) + axios + .get(`/SASjsApi/user/${appContext.userId}`) + .then((res: any) => { + setUser(res.data) + }) + .catch((err) => { + console.log(err) + }) + .finally(() => { + setIsLoading(false) + }) + } }, [appContext.userId]) const handleChange = (event: any) => { diff --git a/web/src/context/appContext.tsx b/web/src/context/appContext.tsx index 6c34e612..cf556a8b 100644 --- a/web/src/context/appContext.tsx +++ b/web/src/context/appContext.tsx @@ -24,39 +24,32 @@ export enum RunTimeType { interface AppContextProps { checkingSession: boolean loggedIn: boolean - setLoggedIn: Dispatch> | null + setLoggedIn?: Dispatch> needsToUpdatePassword: boolean - setNeedsToUpdatePassword: Dispatch> | null - userId: number - setUserId: Dispatch> | null + setNeedsToUpdatePassword?: Dispatch> + userId?: string + setUserId?: Dispatch> username: string - setUsername: Dispatch> | null + setUsername?: Dispatch> displayName: string - setDisplayName: Dispatch> | null + setDisplayName?: Dispatch> isAdmin: boolean - setIsAdmin: Dispatch> | null + setIsAdmin?: Dispatch> mode: ModeType runTimes: RunTimeType[] - logout: (() => void) | null + logout?: () => void } export const AppContext = createContext({ checkingSession: false, loggedIn: false, - setLoggedIn: null, needsToUpdatePassword: false, - setNeedsToUpdatePassword: null, - userId: 0, - setUserId: null, + userId: '', username: '', - setUsername: null, displayName: '', - setDisplayName: null, isAdmin: false, - setIsAdmin: null, mode: ModeType.Server, - runTimes: [], - logout: null + runTimes: [] }) const AppContextProvider = (props: { children: ReactNode }) => { @@ -64,7 +57,7 @@ const AppContextProvider = (props: { children: ReactNode }) => { const [checkingSession, setCheckingSession] = useState(false) const [loggedIn, setLoggedIn] = useState(false) const [needsToUpdatePassword, setNeedsToUpdatePassword] = useState(false) - const [userId, setUserId] = useState(0) + const [userId, setUserId] = useState() const [username, setUsername] = useState('') const [displayName, setDisplayName] = useState('') const [isAdmin, setIsAdmin] = useState(false) diff --git a/web/src/utils/helper.ts b/web/src/utils/helper.ts index 27f6ae9d..45a63a40 100644 --- a/web/src/utils/helper.ts +++ b/web/src/utils/helper.ts @@ -6,13 +6,13 @@ export const findExistingPermission = ( ) => { for (const permission of existingPermissions) { if ( - permission.user?.id === newPermission.principalId && + permission.user?.uid === newPermission.principalId && hasSameCombination(permission, newPermission) ) return permission if ( - permission.group?.groupId === newPermission.principalId && + permission.group?.uid === newPermission.principalId && hasSameCombination(permission, newPermission) ) return permission @@ -27,13 +27,13 @@ export const findUpdatingPermission = ( ) => { for (const permission of existingPermissions) { if ( - permission.user?.id === newPermission.principalId && + permission.user?.uid === newPermission.principalId && hasDifferentSetting(permission, newPermission) ) return permission if ( - permission.group?.groupId === newPermission.principalId && + permission.group?.uid === newPermission.principalId && hasDifferentSetting(permission, newPermission) ) return permission diff --git a/web/src/utils/types.ts b/web/src/utils/types.ts index 8a10b5a1..07d1fefe 100644 --- a/web/src/utils/types.ts +++ b/web/src/utils/types.ts @@ -1,12 +1,12 @@ export interface UserResponse { - id: number + uid: string username: string displayName: string isAdmin: boolean } export interface GroupResponse { - groupId: number + uid: string name: string description: string } @@ -17,7 +17,7 @@ export interface GroupDetailsResponse extends GroupResponse { } export interface PermissionResponse { - permissionId: number + uid: string path: string type: string setting: string @@ -30,7 +30,7 @@ export interface RegisterPermissionPayload { type: string setting: string principalType: string - principalId: number + principalId: string } export interface TreeNode {