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

Merge pull request #520 from logto-io/gao-remove-resource-scopes

refactor(core): remove resource scopes
This commit is contained in:
Gao Sun 2022-04-08 16:15:50 +08:00 committed by GitHub
commit 91c79bb44c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 8 additions and 225 deletions

View file

@ -8,7 +8,6 @@ import { Provider, errors } from 'oidc-provider';
import postgresAdapter from '@/oidc/adapter';
import { findResourceByIndicator } from '@/queries/resource';
import { findAllScopesWithResourceId } from '@/queries/scope';
import { findUserById } from '@/queries/user';
import { routes } from '@/routes/consts';
@ -47,20 +46,18 @@ export default async function initOidc(app: Koa): Promise<Provider> {
// Disable the auto use of authorization_code granted resource feature
// https://github.com/panva/node-oidc-provider/blob/main/docs/README.md#usegrantedresource
useGrantedResource: () => false,
getResourceServerInfo: async (ctx, indicator) => {
getResourceServerInfo: async (_, indicator) => {
const resourceServer = await findResourceByIndicator(indicator);
if (!resourceServer) {
throw new errors.InvalidTarget();
}
const { id, accessTokenTtl: accessTokenTTL } = resourceServer;
const scopes = await findAllScopesWithResourceId(id);
const scope = scopes.map(({ name }) => name).join(' ');
const { accessTokenTtl: accessTokenTTL } = resourceServer;
return {
accessTokenFormat: 'jwt',
scope,
scope: '',
accessTokenTTL,
};
},

View file

@ -1,99 +0,0 @@
import { ResourceScopes } from '@logto/schemas';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
import { DeletionError } from '@/errors/SlonikError';
import { mockScope } from '@/utils/mock';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
import { findAllScopesWithResourceId, insertScope, deleteScopeById } from './scope';
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
jest.mock('@/database/pool', () =>
createMockPool({
query: async (sql, values) => {
return mockQuery(sql, values);
},
})
);
describe('scope query', () => {
const { table, fields } = convertToIdentifiers(ResourceScopes);
it('findAllScopesWithResourceId', async () => {
const expectSql = sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.resourceId}=$1
`;
mockQuery.mockImplementationOnce(async (sql, values) => {
expectSqlAssert(sql, expectSql.sql);
expect(values).toEqual([mockScope.resourceId]);
return createMockQueryResult([mockScope]);
});
await expect(findAllScopesWithResourceId(mockScope.resourceId)).resolves.toEqual([mockScope]);
});
it('insertScope', async () => {
const expectSql = sql`
insert into ${table} (${sql.join(Object.values(fields), sql`, `)})
values (${sql.join(
Object.values(fields).map((_, index) => `$${index + 1}`),
sql`, `
)})
returning *
`;
mockQuery.mockImplementationOnce(async (sql, values) => {
expectSqlAssert(sql, expectSql.sql);
expect(values).toEqual(
ResourceScopes.fieldKeys.map((k) => convertToPrimitiveOrSql(k, mockScope[k]))
);
return createMockQueryResult([mockScope]);
});
await expect(insertScope(mockScope)).resolves.toEqual(mockScope);
});
it('deleteScopeById', async () => {
const id = 'foo';
const expectSql = sql`
delete from ${table}
where ${fields.id}=$1
`;
mockQuery.mockImplementationOnce(async (sql, values) => {
expectSqlAssert(sql, expectSql.sql);
expect(values).toEqual([id]);
return createMockQueryResult([mockScope]);
});
await deleteScopeById(id);
});
it('deleteScopeById throw error if return row count is 0', async () => {
const id = 'foo';
const expectSql = sql`
delete from ${table}
where ${fields.id}=$1
`;
mockQuery.mockImplementationOnce(async (sql, values) => {
expectSqlAssert(sql, expectSql.sql);
expect(values).toEqual([id]);
return createMockQueryResult([]);
});
await expect(deleteScopeById(id)).rejects.toMatchError(
new DeletionError(ResourceScopes.table, id)
);
});
});

View file

@ -1,35 +0,0 @@
import { ResourceScope, CreateResourceScope, ResourceScopes } from '@logto/schemas';
import { sql } from 'slonik';
import { buildInsertInto } from '@/database/insert-into';
import pool from '@/database/pool';
import { convertToIdentifiers } from '@/database/utils';
import { DeletionError } from '@/errors/SlonikError';
const { table, fields } = convertToIdentifiers(ResourceScopes);
export const findAllScopesWithResourceId = async (resourceId: string) =>
pool.any<ResourceScope>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.resourceId}=${resourceId}
`);
export const insertScope = buildInsertInto<CreateResourceScope, ResourceScope>(
pool,
ResourceScopes,
{
returning: true,
}
);
export const deleteScopeById = async (id: string) => {
const { rowCount } = await pool.query(sql`
delete from ${table}
where ${fields.id}=${id}
`);
if (rowCount < 1) {
throw new DeletionError(ResourceScopes.table, id);
}
};

View file

@ -1,6 +1,6 @@
import { Resource, CreateResource } from '@logto/schemas';
import { mockResource, mockScope } from '@/utils/mock';
import { mockResource } from '@/utils/mock';
import { createRequester } from '@/utils/test-utils';
import resourceRoutes from './resource';
@ -24,10 +24,6 @@ jest.mock('@/queries/resource', () => ({
deleteResourceById: jest.fn(),
}));
jest.mock('@/queries/scope', () => ({
findAllScopesWithResourceId: jest.fn(async () => [mockScope]),
}));
jest.mock('@/utils/id', () => ({
// eslint-disable-next-line unicorn/consistent-function-scoping
buildIdGenerator: jest.fn(() => () => 'randomId'),
@ -83,7 +79,6 @@ describe('resource routes', () => {
expect(response.status).toEqual(200);
expect(response.body).toEqual({
...mockResource,
scopes: [mockScope],
});
});
@ -102,7 +97,6 @@ describe('resource routes', () => {
name,
indicator,
accessTokenTtl,
scopes: [mockScope],
});
});

View file

@ -11,7 +11,6 @@ import {
updateResourceById,
deleteResourceById,
} from '@/queries/resource';
import { findAllScopesWithResourceId } from '@/queries/scope';
import { buildIdGenerator } from '@/utils/id';
import { AuthedRouter } from './types';
@ -60,12 +59,8 @@ export default function resourceRoutes<T extends AuthedRouter>(router: T) {
params: { id },
} = ctx.guard;
const [resource, scopes] = await Promise.all([
findResourceById(id),
findAllScopesWithResourceId(id),
]);
ctx.body = { ...resource, scopes };
const resource = await findResourceById(id);
ctx.body = resource;
return next();
}
@ -83,12 +78,8 @@ export default function resourceRoutes<T extends AuthedRouter>(router: T) {
body,
} = ctx.guard;
const [scopes, resource] = await Promise.all([
findAllScopesWithResourceId(id),
updateResourceById(id, body),
]);
ctx.body = { ...resource, scopes };
const resource = await updateResourceById(id, body);
ctx.body = resource;
return next();
}

View file

@ -5,7 +5,6 @@ import {
Application,
ApplicationType,
Resource,
ResourceScope,
Role,
Setting,
SignInExperience,
@ -141,13 +140,6 @@ export const mockResource: Resource = {
accessTokenTtl: 3600,
};
export const mockScope: ResourceScope = {
id: 'foo',
name: 'read:user',
description: 'read:user',
resourceId: 'logto_api',
};
export const mockRole: Role = {
name: 'admin',
description: 'admin',

View file

@ -5,7 +5,6 @@ export * from './application';
export * from './connector';
export * from './oidc-model-instance';
export * from './passcode';
export * from './resource-scope';
export * from './resource';
export * from './role';
export * from './setting';

View file

@ -1,39 +0,0 @@
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
import { z } from 'zod';
import { GeneratedSchema, Guard } from '../foundations';
export type CreateResourceScope = {
id: string;
name: string;
description: string;
resourceId: string;
};
export type ResourceScope = {
id: string;
name: string;
description: string;
resourceId: string;
};
const createGuard: Guard<CreateResourceScope> = z.object({
id: z.string(),
name: z.string(),
description: z.string(),
resourceId: z.string(),
});
export const ResourceScopes: GeneratedSchema<CreateResourceScope> = Object.freeze({
table: 'resource_scopes',
tableSingular: 'resource_scope',
fields: {
id: 'id',
name: 'name',
description: 'description',
resourceId: 'resource_id',
},
fieldKeys: ['id', 'name', 'description', 'resourceId'],
createGuard,
});

View file

@ -1,17 +0,0 @@
create table resource_scopes (
id varchar(24) not null,
name varchar(64) not null,
description text not null,
resource_id varchar(24) not null,
primary key (id),
constraint fk__resource_scopes__resource_id
foreign key (resource_id)
references resources(id)
on delete cascade
);
create unique index resource_scopes__resource_id_name
on resource_scopes (
resource_id,
name
);