0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(core): added a new endpoint to replace user roles (#3203)

This commit is contained in:
Murat Gözel 2023-02-24 12:55:55 +03:00 committed by GitHub
parent dde0ae1ae7
commit 1db6177f3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 1 deletions

View file

@ -6,6 +6,7 @@ import type {
Resource, Resource,
Role, Role,
Scope, Scope,
UsersRole,
} from '@logto/schemas'; } from '@logto/schemas';
import { ApplicationType } from '@logto/schemas'; import { ApplicationType } from '@logto/schemas';
@ -72,6 +73,13 @@ export const mockRole: Role = {
description: 'admin', description: 'admin',
}; };
export const mockRole2: Role = {
tenantId: 'fake_tenant',
id: 'role_id2',
name: 'admin2',
description: 'admin2',
};
export const mockAdminConsoleData: AdminConsoleData = { export const mockAdminConsoleData: AdminConsoleData = {
demoChecked: false, demoChecked: false,
applicationCreated: false, applicationCreated: false,
@ -93,3 +101,10 @@ export const mockPasscode: Passcode = {
tryCount: 2, tryCount: 2,
createdAt: 10, createdAt: 10,
}; };
export const mockUserRole: UsersRole = {
tenantId: 'fake_tenant',
id: 'user_role_id',
userId: 'foo',
roleId: 'role_id',
};

View file

@ -1,6 +1,6 @@
import { pickDefault } from '@logto/shared/esm'; import { pickDefault } from '@logto/shared/esm';
import { mockRole, mockUser } from '#src/__mocks__/index.js'; import { mockRole, mockUser, mockRole2, mockUserRole } from '#src/__mocks__/index.js';
import { mockId, mockStandardId } from '#src/test-utils/nanoid.js'; import { mockId, mockStandardId } from '#src/test-utils/nanoid.js';
import { MockTenant } from '#src/test-utils/tenant.js'; import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js'; import { createRequester } from '#src/utils/test-utils.js';
@ -50,6 +50,18 @@ describe('user role routes', () => {
]); ]);
}); });
it('PUT /users/:id/roles', async () => {
findUsersRolesByUserId.mockResolvedValueOnce([mockUserRole]);
const response = await roleRequester.put(`/users/${mockUser.id}/roles`).send({
roleIds: [mockRole2.id],
});
expect(response.status).toEqual(200);
expect(deleteUsersRolesByUserIdAndRoleId).toHaveBeenCalledWith(mockUser.id, mockRole.id);
expect(insertUsersRoles).toHaveBeenCalledWith([
{ id: mockId, userId: mockUser.id, roleId: mockRole2.id },
]);
});
it('DELETE /users/:id/roles/:roleId', async () => { it('DELETE /users/:id/roles/:roleId', async () => {
const response = await roleRequester.delete(`/users/${mockUser.id}/roles/${mockRole.id}`); const response = await roleRequester.delete(`/users/${mockUser.id}/roles/${mockRole.id}`);
expect(response.status).toEqual(204); expect(response.status).toEqual(204);

View file

@ -99,6 +99,44 @@ export default function adminUserRoleRoutes<T extends AuthedRouter>(
} }
); );
router.put(
'/users/:userId/roles',
koaGuard({
params: object({ userId: string() }),
body: object({ roleIds: string().min(1).array() }),
}),
async (ctx, next) => {
const {
params: { userId },
body: { roleIds },
} = ctx.guard;
await findUserById(userId);
const usersRoles = await findUsersRolesByUserId(userId);
// Only add the ones that doesn't exist
const roleIdsToAdd = roleIds.filter(
(roleId) => !usersRoles.some(({ roleId: _roleId }) => _roleId === roleId)
);
// Remove existing roles that isn't wanted by user anymore
const roleIdsToRemove = usersRoles
.filter(({ roleId }) => !roleIds.includes(roleId))
.map(({ roleId }) => roleId);
await Promise.all(roleIdsToAdd.map(async (roleId) => findRoleById(roleId)));
await Promise.all(
roleIdsToRemove.map(async (roleId) => deleteUsersRolesByUserIdAndRoleId(userId, roleId))
);
await insertUsersRoles(
roleIdsToAdd.map((roleId) => ({ id: generateStandardId(), userId, roleId }))
);
ctx.status = 200;
return next();
}
);
router.delete( router.delete(
'/users/:userId/roles/:roleId', '/users/:userId/roles/:roleId',
koaGuard({ koaGuard({