Skip to content

Commit

Permalink
Internal privilege improvements
Browse files Browse the repository at this point in the history
* Added two privileges for one-time-token use:
  `a12n:one-time-token:generate` and `a12n-one-time-token:exchange`,
  these both required the `admin` privilege. There's no bc break here
  as the original `admin` privilege still covers these new ones.
* It wasn't possible to see a full principal even if a user has the
  `a12n:principal:list` privilege.
  • Loading branch information
evert committed Nov 10, 2023
1 parent 1f3c938 commit 1403d68
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 47 deletions.
1 change: 1 addition & 0 deletions src/middleware/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export default function(): Middleware {
}
// We are logged in!
ctx.auth = new AuthHelper(token.principal);
ctx.privileges = await privilegeService.get(ctx.auth.principal!);

return next();

Expand Down
19 changes: 19 additions & 0 deletions src/migrations/20231109012626_new_privileges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Knex } from 'knex';

export async function up(knex: Knex): Promise<void> {
await knex('privileges')
.insert({privilege: 'a12n:one-time-token:generate', description: 'Create a token for an arbitrary user, this token grants full access to this account.'});

await knex('privileges')
.insert({privilege: 'a12n:one-time-token:exchange', description: 'Exchange a one-time-token for a OAuth2 access token.'});
}


export async function down(knex: Knex): Promise<void> {

await knex('privileges')
.delete()
.whereIn('privileges', ['a12n:one-time-token:generate', 'a12n:one-time-token:exchange']);

}

2 changes: 1 addition & 1 deletion src/one-time-token/controller/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class OneTimeTokenExchangeController extends Controller {

async post(ctx: Context<OtteRequest>) {

ctx.privileges.require('admin');
ctx.privileges.require('a12n:one-time-token:exchange');
const principalService = new PrincipalService(ctx.privileges);

if (!ctx.request.body.token) {
Expand Down
2 changes: 1 addition & 1 deletion src/one-time-token/controller/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class OneTimeTokenController extends Controller {

async post(ctx: Context<any>) {

ctx.privileges.require('admin');
ctx.privileges.require('a12n:one-time-token:generate');

const principalService = new PrincipalService(ctx.privileges);
const user = await principalService.findByExternalId(ctx.params.id, 'user');
Expand Down
2 changes: 1 addition & 1 deletion src/principal/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export class PrincipalService {
*/
async findGroupsForPrincipal(principal: Principal): Promise<Group[]> {

this.privileges.require('admin');
this.privileges.require('a12n:principals:list');
const result = await db('principals')
.select('principals.*')
.innerJoin('group_members', { 'principals.id': 'group_members.group_id'})
Expand Down
11 changes: 7 additions & 4 deletions src/privilege/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export type PrivilegeEntry = {
}

export type InternalPrivilege =
'admin' |
'a12n:principals:list' |
'a12n:principals:create' |
'a12n:principals:update';
| 'admin'
| 'a12n:principals:list'
| 'a12n:principals:create'
| 'a12n:principals:update'
| 'a12n:one-time-token:generate'
| 'a12n:one-time-token:exchange'

48 changes: 12 additions & 36 deletions src/user/controller/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import Controller from '@curveball/controller';
import { Context } from '@curveball/core';
import * as privilegeService from '../../privilege/service';
import * as userHal from '../formats/hal';
import * as appHal from '../../app/formats/hal';
import * as groupHal from '../../group/formats/hal';
import * as userService from '../service';
import { PrincipalService } from '../../principal/service';

Expand All @@ -28,7 +26,7 @@ class UserController extends Controller {
async get(ctx: Context) {

const principalService = new PrincipalService(ctx.privileges);
const principal = await principalService.findByExternalId(ctx.params.id);
const principal = await principalService.findByExternalId(ctx.params.id, 'user');

let hasControl = false;
let hasPassword = false;
Expand All @@ -45,39 +43,17 @@ class UserController extends Controller {
}

const principalPrivileges = await privilegeService.get(principal);
// This endpoint supports rendering groups and apps, for backwards
// compatibility. This will be removed in the future
switch(principal.type) {
case 'user' :
ctx.response.body = userHal.item(
principal,
principalPrivileges.getAll(),
hasControl,
hasPassword,
isAdmin,
await principalService.findGroupsForPrincipal(principal),
);
break;
case 'group' : {
const members = await principalService.findMembers(principal);
ctx.response.body = groupHal.item(
principal,
principalPrivileges.getAll(),
isAdmin,
await principalService.findGroupsForPrincipal(principal),
members,
);
break;
}
case 'app' :
ctx.response.body = appHal.item(
principal,
principalPrivileges.getAll(),
isAdmin,
await principalService.findGroupsForPrincipal(principal),
);
break;
}

const currentUserPrivileges = ctx.privileges;

ctx.response.body = userHal.item(
principal,
principalPrivileges.getAll(),
hasControl,
hasPassword,
currentUserPrivileges,
await principalService.findGroupsForPrincipal(principal),
);

}

Expand Down
13 changes: 9 additions & 4 deletions src/user/formats/hal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PrivilegeMap } from '../../privilege/types';
import { Principal, Group, User } from '../../types';
import { HalResource } from 'hal-types';
import { LazyPrivilegeBox } from '../../privilege/service';

export function collection(users: User[]): HalResource {

Expand Down Expand Up @@ -32,7 +33,7 @@ export function collection(users: User[]): HalResource {
* we're generating the repsonse for, or if the current authenticated user
* has full admin privileges
*/
export function item(user: User, privileges: PrivilegeMap, hasControl: boolean, hasPassword: boolean, isAdmin: boolean, groups: Group[]): HalResource {
export function item(user: User, privileges: PrivilegeMap, hasControl: boolean, hasPassword: boolean, currentUserPrivileges: LazyPrivilegeBox, groups: Group[]): HalResource {

const hal: HalResource = {
_links: {
Expand All @@ -58,15 +59,19 @@ export function item(user: User, privileges: PrivilegeMap, hasControl: boolean,
privileges
};

if (hasControl) {
hal.hasPassword = hasPassword;
if (hasControl || currentUserPrivileges.has('a12n:one-time-token:generate')) {
hal._links['one-time-token'] = {
href: `${user.href}/one-time-token`,
title: 'Generate a one-time login token.',
hints: {
allow: ['POST'],
}
};
}

if (hasControl) {
hal.hasPassword = hasPassword;

hal._links['access-token'] = {
href: `${user.href}/access-token`,
title: 'Generate an access token for this user.',
Expand All @@ -80,7 +85,7 @@ export function item(user: User, privileges: PrivilegeMap, hasControl: boolean,
title: 'App Permissions',
};
}
if (isAdmin) {
if (currentUserPrivileges.has('admin')) {
hal._links['password'] = {
href: `${user.href}/password`,
title: 'Change user\'s password',
Expand Down

0 comments on commit 1403d68

Please sign in to comment.