From 1745f850635ecb79f8cc467f37c57c69935807bc Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Sun, 27 Oct 2024 20:29:31 -0400 Subject: [PATCH] Generating arbitrary access-tokens is now behind a special privilege. Before this change it required the 'admin' privilege. Users that had 'admin' automatically inherit all privileges so this is not a BC break. --- .../20241027202400_new_privileges.ts | 18 ++++++++++++++++++ src/oauth2/controller/user-access-token.ts | 4 ++-- src/privilege/types.ts | 3 ++- src/server-settings.ts | 2 +- src/user/formats/hal.ts | 15 +++++++++++---- 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 src/migrations/20241027202400_new_privileges.ts diff --git a/src/migrations/20241027202400_new_privileges.ts b/src/migrations/20241027202400_new_privileges.ts new file mode 100644 index 00000000..a3705d03 --- /dev/null +++ b/src/migrations/20241027202400_new_privileges.ts @@ -0,0 +1,18 @@ +import { Knex } from 'knex'; + +const privilege = 'a12n:access-token:generate'; + +export async function up(knex: Knex): Promise { + await knex('privileges') + .insert({privilege, description: 'Allows a user to create a valid access-token for another user without consent. This privilege allows full control of other accounts and should never be given to third parties.'}); + +} + +export async function down(knex: Knex): Promise { + + await knex('privileges') + .delete() + .whereIn('privileges', [privilege]); + +} + diff --git a/src/oauth2/controller/user-access-token.ts b/src/oauth2/controller/user-access-token.ts index 64548284..cc207ae9 100644 --- a/src/oauth2/controller/user-access-token.ts +++ b/src/oauth2/controller/user-access-token.ts @@ -14,8 +14,8 @@ class UserAccessTokenController extends Controller { const principalService = new PrincipalService(ctx.privileges); const user = await principalService.findByExternalId(ctx.params.id, 'user'); - if (ctx.auth.equals(user) && !ctx.privileges.has('admin')) { - throw new Forbidden('You can only generate OAuth2 access tokens for yourself with this endpoint (unless you have the \'admin\' privilege (which you haven\'t))'); + if (!ctx.auth.equals(user) && !ctx.privileges.has('a12n:access-token:generate')) { + throw new Forbidden('You can only generate OAuth2 access tokens for yourself with this endpoint (unless you have the \'a12n:access-token:generate\' privilege (which you haven\'t))'); } const token = await oauth2Service.generateTokenDeveloperToken({ diff --git a/src/privilege/types.ts b/src/privilege/types.ts index 4a16db7d..157aa8b9 100644 --- a/src/privilege/types.ts +++ b/src/privilege/types.ts @@ -22,4 +22,5 @@ export type InternalPrivilege = | 'a12n:principals:update' | 'a12n:one-time-token:generate' | 'a12n:one-time-token:exchange' - | 'a12n:user:change-password'; + | 'a12n:user:change-password' + | 'a12n:access-token:generate'; diff --git a/src/server-settings.ts b/src/server-settings.ts index 5528b12e..59fbda8f 100644 --- a/src/server-settings.ts +++ b/src/server-settings.ts @@ -256,7 +256,7 @@ export function requireSetting(setting: T): Settings[T } else { msg+'There is literally no way to actually do this, and this is a bug. DM me for a prize.'; } - throw msg; + throw new Error(msg); } return value; diff --git a/src/user/formats/hal.ts b/src/user/formats/hal.ts index f0ee13ea..57edf0f3 100644 --- a/src/user/formats/hal.ts +++ b/src/user/formats/hal.ts @@ -72,6 +72,17 @@ export function item(user: User, privileges: PrivilegeMap, hasControl: boolean, }; } + if (hasControl || currentUserPrivileges.has('a12n:access-token:generate', user.href)) { + hal._links['access-token'] = { + href: `${user.href}/access-token`, + title: 'Generate an access token for this user.', + hints: { + allow: ['POST'], + } + } + + } + if (hasControl) { hal.hasPassword = hasPassword; @@ -79,10 +90,6 @@ export function item(user: User, privileges: PrivilegeMap, hasControl: boolean, href: `${user.href}/auth-factor`, title: 'List of authentication methods / authentication factors for a user', }; - hal._links['access-token'] = { - href: `${user.href}/access-token`, - title: 'Generate an access token for this user.', - }; hal._links['active-sessions'] = { href: `${user.href}/sessions`, title: 'Active user sessions'