Add user authentication and management features

- Introduce user authentication with form-based login, including a default admin user.
- Implement UserEntity, UserRepository, and UserService for user management.
- Create RESTful endpoints for user listing and creation, restricted to admin role.
- Enhance OpenAPI specification to document new authentication and user management endpoints.
- Add frontend components for login and user management, including protected routes.
- Implement context and hooks for managing authentication state in the React application.
- Include unit tests for user service and authentication logic.
This commit is contained in:
Konrad Neitzel
2026-02-22 11:28:04 +01:00
parent 4c1584ec27
commit 2c61ab5fc9
28 changed files with 1252 additions and 32 deletions
+24
View File
@@ -3,6 +3,23 @@ quarkus:
name: roleplay
http:
root-path: /
# Form-based auth: SPA mode (no redirects; 200/401). Session in encrypted cookie.
auth:
form:
enabled: true
login-page: ""
landing-page: ""
error-page: ""
http-only-cookie: false
session:
encryption-key: roleplay-session-secret-key-min-16chars
permission:
form-login:
paths: /j_security_check
policy: permit
api:
paths: /api/v1/*
policy: authenticated
datasource:
db-kind: h2
jdbc:
@@ -28,3 +45,10 @@ quarkus:
level: DEBUG
"org.jboss.resteasy.reactive.client.logging":
level: DEBUG
# Login debugging: form auth and security identity
"io.quarkus.vertx.http.runtime.security":
level: DEBUG
"io.quarkus.security":
level: DEBUG
"de.neitzel.roleplay":
level: INFO
@@ -6,5 +6,6 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.24.xsd">
<include file="db/migration/v001__scenarios_and_characters.xml"/>
<include file="db/migration/v002__users_and_roles.xml"/>
</databaseChangeLog>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.24.xsd">
<changeSet id="002-1-create-rp-user-table" author="roleplay">
<createTable tableName="rp_user">
<column name="id" type="uuid">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="username" type="varchar(255)">
<constraints nullable="false" unique="true"/>
</column>
<column name="password" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="role" type="varchar(50)">
<constraints nullable="false"/>
</column>
</createTable>
<comment>Stores application users for form-based auth. Default login: admin / changeme.</comment>
</changeSet>
<changeSet id="002-2-seed-admin-user" author="roleplay">
<insert tableName="rp_user">
<column name="id" value="11111111-1111-1111-1111-111111111111"/>
<column name="username" value="admin"/>
<column name="password" value="$2a$10$dFA336yJOw3.pogwU.vXVu3BRRfBr1yjhGC6O6nfC2qGFA5e29.cO"/>
<column name="role" value="admin"/>
</insert>
<rollback>
<delete tableName="rp_user">
<where>id = '11111111-1111-1111-1111-111111111111'</where>
</delete>
</rollback>
</changeSet>
</databaseChangeLog>
@@ -23,6 +23,10 @@ tags:
description: List and retrieve saved scenario templates
- name: characters
description: List and retrieve saved character templates
- name: auth
description: Authentication (current user, logout)
- name: users
description: User management (admin only)
paths:
@@ -411,6 +415,84 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/me:
get:
operationId: getAuthMe
summary: Current user info
description: Returns the authenticated user's username and roles. Requires session cookie.
tags:
- auth
responses:
"200":
description: Current user.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthMeResponse'
"401":
description: Not authenticated.
/auth/logout:
post:
operationId: logout
summary: Log out
description: Clears the session cookie. Requires authenticated user.
tags:
- auth
responses:
"204":
description: Logged out.
"401":
description: Not authenticated.
/admin/users:
get:
operationId: listUsers
summary: List users (admin only)
description: Returns all users (id, username, role). Admin role required.
tags:
- users
responses:
"200":
description: List of users.
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
"401":
description: Not authenticated.
"403":
description: Forbidden (admin only).
post:
operationId: createUser
summary: Create user (admin only)
description: Creates a new user with the given username, password, and role.
tags:
- users
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
"201":
description: User created.
content:
application/json:
schema:
$ref: '#/components/schemas/UserSummary'
"400":
description: Invalid request (e.g. username already exists).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
"401":
description: Not authenticated.
"403":
description: Forbidden (admin only).
components:
parameters:
@@ -1017,6 +1099,70 @@ components:
- type
- title
# ─── Auth and users ───────────────────────────────────────────────────────
AuthMeResponse:
type: object
description: Current authenticated user.
properties:
username:
type: string
description: Login name.
roles:
type: array
items:
type: string
description: Assigned roles (e.g. admin, user).
required:
- username
- roles
UserSummary:
type: object
description: User without password (for list and create response).
properties:
id:
type: string
format: uuid
description: User UUID.
username:
type: string
description: Login name.
role:
type: string
description: Role (admin or user).
required:
- id
- username
- role
UserListResponse:
type: array
items:
$ref: '#/components/schemas/UserSummary'
description: List of users (admin only).
CreateUserRequest:
type: object
description: Request to create a new user (admin only).
properties:
username:
type: string
description: Login name.
password:
type: string
description: Plain-text password (hashed on server).
role:
type: string
enum:
- admin
- user
description: Role to assign.
required:
- username
- password
- role
# ─── Error ────────────────────────────────────────────────────────────────
ErrorResponse: