From 685f9380cc4938e60704c1f323a4c252135215f7 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Tue, 21 Nov 2023 15:27:27 +0000 Subject: [PATCH] Added new privilege for changing passwords. --- changelog.md | 1 + src/migrations/20231121134632_new_privileges.ts | 17 +++++++++++++++++ src/privilege/types.ts | 2 +- src/user/controller/password.ts | 12 ++++++++++-- src/user/controller/privileges.ts | 1 - src/user/formats/hal.ts | 2 +- 6 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/migrations/20231121134632_new_privileges.ts diff --git a/changelog.md b/changelog.md index 51a6deb3..6010228e 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ Changelog covers these new ones. * It wasn't possible to see a full principal even if a user had `a12n:principal:list` privilege. +* Added new privilege for changing passwords: `a12n:user:change-password`. 0.24.0 (2023-11-09) diff --git a/src/migrations/20231121134632_new_privileges.ts b/src/migrations/20231121134632_new_privileges.ts new file mode 100644 index 00000000..933c03ce --- /dev/null +++ b/src/migrations/20231121134632_new_privileges.ts @@ -0,0 +1,17 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex('privileges') + .insert({privilege: 'a12n:user:change-password', description: 'Allow changing a users\' password.'}); + +} + + +export async function down(knex: Knex): Promise { + + await knex('privileges') + .delete() + .whereIn('privileges', ['a12n:user:change-password']); + +} + diff --git a/src/privilege/types.ts b/src/privilege/types.ts index 7094d3d0..06d95b6b 100644 --- a/src/privilege/types.ts +++ b/src/privilege/types.ts @@ -22,4 +22,4 @@ export type InternalPrivilege = | 'a12n:principals:update' | 'a12n:one-time-token:generate' | 'a12n:one-time-token:exchange' - + | 'a12n:user:change-password'; diff --git a/src/user/controller/password.ts b/src/user/controller/password.ts index 843a7982..c1b8245c 100644 --- a/src/user/controller/password.ts +++ b/src/user/controller/password.ts @@ -2,17 +2,25 @@ import Controller from '@curveball/controller'; import { Context } from '@curveball/core'; import { PrincipalService } from '../../principal/service'; import * as userService from '../service'; +import { UnprocessableEntity } from '@curveball/http-errors'; class UserPasswordController extends Controller { async put(ctx: Context) { - ctx.privileges.require('admin'); - const userBody: any = ctx.request.body; const principalService = new PrincipalService(ctx.privileges); const user = await principalService.findByExternalId(ctx.params.id, 'user'); + ctx.privileges.require('a12n:user:change-password', user.href); + + if (!userBody.newPassword || typeof userBody.newPassword !== 'string') { + throw new UnprocessableEntity('The "newPassword" property is required.'); + } + if (userBody.newPassword.length < 8) { + throw new UnprocessableEntity('Passwords must be at least 8 characters.'); + } + const password = userBody.newPassword; await userService.updatePassword(user, password); diff --git a/src/user/controller/privileges.ts b/src/user/controller/privileges.ts index 00e2de5d..946dddb0 100644 --- a/src/user/controller/privileges.ts +++ b/src/user/controller/privileges.ts @@ -5,7 +5,6 @@ import * as privilegeService from '../../privilege/service'; import { PrivilegeMap } from '../../privilege/types'; import * as hal from '../formats/hal'; import { PrincipalService } from '../../principal/service'; -// import * as groupService from '../../group/service'; type PolicyForm = { policyBody: string; diff --git a/src/user/formats/hal.ts b/src/user/formats/hal.ts index f13eee3e..cd0bd338 100644 --- a/src/user/formats/hal.ts +++ b/src/user/formats/hal.ts @@ -85,7 +85,7 @@ export function item(user: User, privileges: PrivilegeMap, hasControl: boolean, title: 'App Permissions', }; } - if (currentUserPrivileges.has('admin')) { + if (currentUserPrivileges.has('a12n:user:change-password', user.href)) { hal._links['password'] = { href: `${user.href}/password`, title: 'Change user\'s password',