0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

refactor(core): update role in authn route for Hasura (#2944)

This commit is contained in:
wangsijie 2023-01-20 16:14:41 +08:00 committed by GitHub
parent b575a45b43
commit 6b5eb4a0e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 8 deletions

View file

@ -1,7 +1,7 @@
import { UsersPasswordEncryptionMethod } from '@logto/schemas';
import { createMockPool } from 'slonik';
import { mockResource, mockScope } from '#src/__mocks__/index.js';
import { mockResource, mockRole, mockScope } from '#src/__mocks__/index.js';
import { mockUser } from '#src/__mocks__/user.js';
import { MockQueries } from '#src/test-utils/tenant.js';
@ -16,6 +16,7 @@ const { encryptUserPassword, createUserLibrary } = await import('./user.js');
const hasUserWithId = jest.fn();
const queries = new MockQueries({
users: { hasUserWithId },
roles: { findRolesByRoleIds: async () => [mockRole] },
scopes: { findScopesByIdsAndResourceId: async () => [mockScope] },
usersRoles: { findUsersRolesByUserId: async () => [] },
rolesScopes: { findRolesScopesByRoleIds: async () => [] },
@ -80,3 +81,11 @@ describe('findUserScopesForResourceId()', () => {
]);
});
});
describe('findUserRoles()', () => {
const { findUserRoles } = createUserLibrary(queries);
it('returns user roles', async () => {
await expect(findUserRoles(mockUser.id)).resolves.toEqual([mockRole]);
});
});

View file

@ -195,6 +195,13 @@ export const createUserLibrary = (queries: Queries) => {
return scopes;
};
const findUserRoles = async (userId: string) => {
const usersRoles = await findUsersRolesByUserId(userId);
const roles = await findRolesByRoleIds(usersRoles.map(({ roleId }) => roleId));
return roles;
};
return {
findUserByIdWithRoles,
generateUserId,
@ -202,5 +209,6 @@ export const createUserLibrary = (queries: Queries) => {
checkIdentifierCollision,
findUsersByRoleName,
findUserScopesForResourceId,
findUserRoles,
};
};

View file

@ -43,7 +43,6 @@ type TokenInfo = {
sub: string;
clientId: unknown;
scopes: string[];
roleNames?: string[];
};
export const verifyBearerTokenFromRequest = async (

View file

@ -1,6 +1,9 @@
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { mockRole } from '#src/__mocks__/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import Libraries from '#src/tenants/Libraries.js';
import { MockTenant } from '#src/test-utils/tenant.js';
const { jest } = import.meta;
const { mockEsmWithActual } = createMockUtils(jest);
@ -12,14 +15,20 @@ const { verifyBearerTokenFromRequest } = await mockEsmWithActual(
})
);
const usersLibraries = {
findUserRoles: jest.fn(async () => [mockRole]),
} satisfies Partial<Libraries['users']>;
const tenantContext = new MockTenant(undefined, {}, { users: usersLibraries });
const { createRequester } = await import('#src/utils/test-utils.js');
const request = createRequester({
anonymousRoutes: await pickDefault(import('#src/routes/authn.js')),
tenantContext,
});
describe('authn route for Hasura', () => {
const mockUserId = 'foo';
const mockExpectedRole = 'some_role';
const mockExpectedRole = mockRole.name;
const mockUnauthorizedRole = 'V';
const keys = Object.freeze({
expectedRole: 'Expected-Role',
@ -32,7 +41,6 @@ describe('authn route for Hasura', () => {
verifyBearerTokenFromRequest.mockResolvedValue({
clientId: 'ok',
sub: mockUserId,
roleNames: [mockExpectedRole],
});
});

View file

@ -13,8 +13,10 @@ import type { AnonymousRouter, RouterInitArgs } from './types.js';
* For now, we only implement the API for Hasura authentication.
*/
export default function authnRoutes<T extends AnonymousRouter>(
...[router, { envSet }]: RouterInitArgs<T>
...[router, { envSet, libraries }]: RouterInitArgs<T>
) {
const { findUserRoles } = libraries.users;
router.get(
'/authn/hasura',
koaGuard({
@ -36,9 +38,11 @@ export default function authnRoutes<T extends AnonymousRouter>(
}
};
const { sub, roleNames } = await verifyToken(resource);
const { sub } = await verifyToken(resource);
const roles = sub ? await findUserRoles(sub) : [];
const roleNames = new Set(roles.map(({ name }) => name));
if (unauthorizedRole && (!expectedRole || !roleNames?.includes(expectedRole))) {
if (unauthorizedRole && (!expectedRole || !roleNames.has(expectedRole))) {
ctx.body = {
'X-Hasura-User-Id':
sub ??
@ -54,7 +58,7 @@ export default function authnRoutes<T extends AnonymousRouter>(
if (expectedRole) {
assertThat(
roleNames?.includes(expectedRole),
roleNames.has(expectedRole),
new RequestError({ code: 'auth.expected_role_not_found', status: 401 })
);
}