Added ability for users to update their password. Minor tidy up.

This commit is contained in:
jd
2026-02-23 20:48:52 +00:00
parent 872a79663b
commit df60ad4552
5 changed files with 73 additions and 65 deletions

View File

@@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { brandColours } from '../utilities/helpers'; import { brandColours } from '../utilities/helpers';
import { size } from 'lodash';
interface InviteEmailProperties { interface InviteEmailProperties {
playerName: string; playerName: string;
@@ -21,6 +20,7 @@ export const InviteEmail = (props: InviteEmailProperties) => (
cellSpacing={0} cellSpacing={0}
cellPadding={0} cellPadding={0}
> >
<tbody>
<tr> <tr>
<td align="center"> <td align="center">
<div <div
@@ -72,6 +72,7 @@ export const InviteEmail = (props: InviteEmailProperties) => (
</div> </div>
</td> </td>
</tr> </tr>
</tbody>
</table> </table>
</div> </div>
); );

View File

@@ -189,7 +189,7 @@ export class MatchOrm {
const player = await orm.players.get(playerId); const player = await orm.players.get(playerId);
sql.transaction(async (tx) => { await sql.transaction(async (tx) => {
const eloRefund = parseInt( const eloRefund = parseInt(
( (
await tx`SELECT elo_change FROM public.match_players WHERE match_id=${matchId.raw} AND player_id = ${playerId.raw}` await tx`SELECT elo_change FROM public.match_players WHERE match_id=${matchId.raw} AND player_id = ${playerId.raw}`

View File

@@ -24,9 +24,15 @@ export class User {
} }
export class UsersOrm { export class UsersOrm {
async create( async create({
{ email, password, playerId }: { email: string; password: string; playerId: PlayerId }, email,
): Promise<User> { password,
playerId,
}: {
email: string;
password: string;
playerId: PlayerId;
}): Promise<User> {
const existingUser: any = first( const existingUser: any = first(
await sql`SELECT id await sql`SELECT id
FROM users FROM users
@@ -100,11 +106,19 @@ export class UsersOrm {
userToUpdate.isAdmin = patch.isAdmin ?? userToUpdate.isAdmin; userToUpdate.isAdmin = patch.isAdmin ?? userToUpdate.isAdmin;
} }
await sql`UPDATE users await sql.transaction(async (tx) => {
await tx`UPDATE users
SET is_active=${userToUpdate.isActive}, SET is_active=${userToUpdate.isActive},
is_admin=${userToUpdate.isAdmin} is_admin=${userToUpdate.isAdmin}
WHERE id = ${id.raw}`; WHERE id = ${id.raw}`;
if (id === claims?.userId && patch.password) {
const passwordHash = await argon2.hash(patch.password);
await tx`UPDATE users
SET pass_hash = ${passwordHash}`;
}
});
return await this.get(id); return await this.get(id);
} }
@@ -133,10 +147,7 @@ export class UsersOrm {
return; return;
} }
async verifyCredentials( async verifyCredentials(email: string, password: string): Promise<{ userId: UserId; refreshCount: string } | null> {
email: string,
password: string,
): Promise<{ userId: UserId; refreshCount: string } | null> {
const dbResult: any = first( const dbResult: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
@@ -168,12 +179,7 @@ export class UsersOrm {
return dbResult.refresh_count === refreshCount; return dbResult.refresh_count === refreshCount;
} }
async changePassword( async changePassword(id: UserId, oldPassword: string | null, newPassword: string, claims?: Claims): Promise<void> {
id: UserId,
oldPassword: string | null,
newPassword: string,
claims?: Claims,
): Promise<void> {
const isAdmin = Claims.test(Claims.ADMIN, claims); const isAdmin = Claims.test(Claims.ADMIN, claims);
if (!(isAdmin || (Claims.test(Claims.USERS.SELF.UPDATE, claims) && id === claims?.userId))) { if (!(isAdmin || (Claims.test(Claims.USERS.SELF.UPDATE, claims) && id === claims?.userId))) {
throw new UnauthorizedError(); throw new UnauthorizedError();

View File

@@ -66,6 +66,6 @@ export function unwrapMethod<T = {}>(
): (r: Request) => Promise<Response> { ): (r: Request) => Promise<Response> {
return async (request: Request) => { return async (request: Request) => {
const unwrappedRequest = await unwrap<T>(request); const unwrappedRequest = await unwrap<T>(request);
return await methodToUnwrap(unwrappedRequest); return methodToUnwrap(unwrappedRequest);
}; };
} }

View File

@@ -14,6 +14,7 @@ export interface CreateUserRequest {
export interface UpdateUserRequest { export interface UpdateUserRequest {
isActive?: boolean; isActive?: boolean;
isAdmin?: boolean; isAdmin?: boolean;
password?: string;
} }
export interface InviteUserRequest { export interface InviteUserRequest {
email: string; email: string;