Skip to content

Commit

Permalink
Generating arbitrary access-tokens is now behind a special privilege.
Browse files Browse the repository at this point in the history
Before this change it required the 'admin' privilege. Users that had
'admin' automatically inherit all privileges so this is not a BC break.
  • Loading branch information
evert committed Oct 28, 2024
1 parent 33f9ef3 commit 1745f85
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 8 deletions.
18 changes: 18 additions & 0 deletions src/migrations/20241027202400_new_privileges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Knex } from 'knex';

const privilege = 'a12n:access-token:generate';

export async function up(knex: Knex): Promise<void> {
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<void> {

await knex('privileges')
.delete()
.whereIn('privileges', [privilege]);

}

4 changes: 2 additions & 2 deletions src/oauth2/controller/user-access-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
3 changes: 2 additions & 1 deletion src/privilege/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 1 addition & 1 deletion src/server-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ export function requireSetting<T extends keyof Settings>(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;
Expand Down
15 changes: 11 additions & 4 deletions src/user/formats/hal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,24 @@ 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'],
}
}

Check failure on line 82 in src/user/formats/hal.ts

View workflow job for this annotation

GitHub Actions / Lint

Missing semicolon

}

if (hasControl) {
hal.hasPassword = hasPassword;

hal._links['auth-factor-collection'] = {
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'
Expand Down

0 comments on commit 1745f85

Please sign in to comment.