0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-04-07 23:01:25 -05:00

feat(core): scope name within the same resource should be unique (#2861)

This commit is contained in:
wangsijie 2023-01-10 13:15:48 +08:00 committed by GitHub
parent 550cbe3436
commit 34aab882c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 90 additions and 1 deletions

View file

@ -44,6 +44,18 @@ export const createScopeQueries = (pool: CommonQueryMethods) => {
${buildResourceConditions(search)}
`);
const findScopeByNameAndResourceId = async (
name: string,
resourceId: string,
excludeScopeId?: string
) =>
pool.maybeOne<Scope>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.resourceId}=${resourceId} and ${fields.name}=${name}
${conditionalSql(excludeScopeId, (id) => sql`and ${fields.id}<>${id}`)}
`);
const findScopesByResourceId = async (resourceId: string) =>
pool.any<Scope>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
@ -94,6 +106,7 @@ export const createScopeQueries = (pool: CommonQueryMethods) => {
return {
findScopes,
countScopes,
findScopeByNameAndResourceId,
findScopesByResourceId,
findScopesByResourceIds,
findScopesByIds,
@ -109,6 +122,7 @@ export const createScopeQueries = (pool: CommonQueryMethods) => {
export const {
findScopes,
countScopes,
findScopeByNameAndResourceId,
findScopesByResourceId,
findScopesByResourceIds,
findScopesByIds,

View file

@ -38,6 +38,7 @@ const { insertScope, updateScopeById } = await mockEsmWithActual('#src/queries/s
insertScope: jest.fn(async () => mockScope),
updateScopeById: jest.fn(async () => mockScope),
deleteScopeById: jest.fn(),
findScopeByNameAndResourceId: jest.fn(),
}));
mockEsm('@logto/core-kit', () => ({

View file

@ -19,10 +19,12 @@ import {
import {
countScopes,
deleteScopeById,
findScopeByNameAndResourceId,
findScopes,
insertScope,
updateScopeById,
} from '#src/queries/scope.js';
import assertThat from '#src/utils/assert-that.js';
import { parseSearchParamsForSearch } from '#src/utils/search.js';
import type { AuthedRouter, RouterInitArgs } from './types.js';
@ -185,6 +187,15 @@ export default function resourceRoutes<T extends AuthedRouter>(...[router]: Rout
body,
} = ctx.guard;
assertThat(
!(await findScopeByNameAndResourceId(body.name, resourceId)),
new RequestError({
code: 'scope.name_exists',
name: body.name,
status: 422,
})
);
ctx.body = await insertScope({
...body,
id: scopeId(),
@ -203,10 +214,21 @@ export default function resourceRoutes<T extends AuthedRouter>(...[router]: Rout
}),
async (ctx, next) => {
const {
params: { scopeId },
params: { scopeId, resourceId },
body,
} = ctx.guard;
if (body.name) {
assertThat(
!(await findScopeByNameAndResourceId(body.name, resourceId, scopeId)),
new RequestError({
code: 'scope.name_exists',
name: body.name,
status: 422,
})
);
}
ctx.body = await updateScopeById(scopeId, body);
return next();

View file

@ -181,6 +181,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -180,6 +180,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role',
user_exists: 'The user id {{userId}} is already been added to this role',
},
scope: {
name_exists: 'The scope name {{name}} is already in use',
},
};
export default errors;

View file

@ -187,6 +187,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -174,6 +174,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -188,6 +188,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -182,6 +182,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -182,6 +182,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -163,6 +163,9 @@ const errors = {
scope_exists: 'The scope id {{scopeId}} has already been added to this role', // UNTRANSLATED
user_exists: 'The user id {{userId}} is already been added to this role', // UNTRANSLATED
},
scope: {
name_exists: 'The scope name {{name}} is already in use', // UNTRANSLATED
},
};
export default errors;

View file

@ -0,0 +1,22 @@
import { sql } from 'slonik';
import type { AlterationScript } from '../lib/types/alteration.js';
const alteration: AlterationScript = {
up: async (pool) => {
await pool.query(sql`
create index scopes__resource_id_name
on scopes (
resource_id,
name
);
`);
},
down: async (pool) => {
await pool.query(sql`
drop index scopes__resource_id_name
`);
},
};
export default alteration;

View file

@ -6,3 +6,9 @@ create table scopes (
created_at timestamptz not null default(now()),
primary key (id)
);
create index scopes__resource_id_name
on scopes (
resource_id,
name
);