feat(domain): add core entities — Club, Member, WorkItem, Shift with state machine
- Create domain entities in WorkClub.Domain/Entities: Club, Member, WorkItem, Shift, ShiftSignup - Implement enums: SportType, ClubRole, WorkItemStatus - Add ITenantEntity interface for multi-tenancy support - Implement state machine validation on WorkItem with C# 14 switch expressions - Valid transitions: Open→Assigned→InProgress→Review→Done, Review→InProgress (rework) - All invalid transitions throw InvalidOperationException - TDD approach: Write tests first, 12/12 passing - Use required properties with explicit Guid/Guid? for foreign keys - DateTimeOffset for timestamps (timezone-aware, multi-tenant friendly) - RowVersion byte[] for optimistic concurrency control - No navigation properties yet (deferred to EF Core task) - No domain events or validation attributes (YAGNI for MVP)
This commit is contained in:
176
.sisyphus/evidence/task-3-verification.txt
Normal file
176
.sisyphus/evidence/task-3-verification.txt
Normal file
@@ -0,0 +1,176 @@
|
||||
Task 3: Keycloak Realm Configuration - Verification Evidence
|
||||
==============================================================
|
||||
|
||||
Date: 2026-03-03
|
||||
Task: Configure Keycloak realm with test users and club memberships
|
||||
|
||||
REALM CONFIGURATION
|
||||
-------------------
|
||||
Realm Name: workclub
|
||||
Status: enabled
|
||||
Keycloak Version: 26.0.0
|
||||
|
||||
CLIENTS CONFIGURED
|
||||
------------------
|
||||
|
||||
1. workclub-api (Backend Confidential Client)
|
||||
- Client ID: workclub-api
|
||||
- Type: confidential
|
||||
- Client Secret: dev-secret-workclub-api-change-in-production
|
||||
- Standard Flow: disabled
|
||||
- Direct Access Grants: disabled
|
||||
- Service Accounts: enabled
|
||||
- Purpose: Backend service-to-service authentication
|
||||
|
||||
2. workclub-app (Frontend Public Client)
|
||||
- Client ID: workclub-app
|
||||
- Type: public
|
||||
- Standard Flow: enabled (OAuth2 Authorization Code Flow)
|
||||
- Direct Access Grants: enabled (for dev testing with password grant)
|
||||
- PKCE: enabled (S256 challenge method)
|
||||
- Redirect URIs: http://localhost:3000/*
|
||||
- Web Origins: http://localhost:3000
|
||||
- Purpose: Frontend SPA authentication
|
||||
|
||||
PROTOCOL MAPPER CONFIGURATION
|
||||
-----------------------------
|
||||
Mapper Name: club-membership
|
||||
Type: oidc-usermodel-attribute-mapper
|
||||
User Attribute: clubs
|
||||
Token Claim Name: clubs
|
||||
JSON Type: JSON (critical - ensures claim is parsed as JSON object)
|
||||
Includes in: ID token, access token, userinfo endpoint
|
||||
|
||||
Configuration:
|
||||
- user.attribute: clubs
|
||||
- claim.name: clubs
|
||||
- jsonType.label: JSON
|
||||
- id.token.claim: true
|
||||
- access.token.claim: true
|
||||
- userinfo.token.claim: true
|
||||
- multivalued: false
|
||||
- aggregate.attrs: false
|
||||
|
||||
TEST USERS CONFIGURED
|
||||
---------------------
|
||||
|
||||
1. admin@test.com
|
||||
Password: testpass123
|
||||
Clubs: {"club-1-uuid": "admin", "club-2-uuid": "member"}
|
||||
Description: Multi-club admin with admin role in club-1, member role in club-2
|
||||
|
||||
2. manager@test.com
|
||||
Password: testpass123
|
||||
Clubs: {"club-1-uuid": "manager"}
|
||||
Description: Single club manager with manager role in club-1
|
||||
|
||||
3. member1@test.com
|
||||
Password: testpass123
|
||||
Clubs: {"club-1-uuid": "member", "club-2-uuid": "member"}
|
||||
Description: Multi-club member with member role in both clubs
|
||||
|
||||
4. member2@test.com
|
||||
Password: testpass123
|
||||
Clubs: {"club-1-uuid": "member"}
|
||||
Description: Single club member with member role in club-1
|
||||
|
||||
5. viewer@test.com
|
||||
Password: testpass123
|
||||
Clubs: {"club-1-uuid": "viewer"}
|
||||
Description: Read-only viewer with viewer role in club-1
|
||||
|
||||
All users:
|
||||
- Email verified: true
|
||||
- Enabled: true
|
||||
- Password hashed with: pbkdf2-sha512, 210000 iterations
|
||||
- No required actions (can login immediately)
|
||||
|
||||
JSON VALIDATION
|
||||
---------------
|
||||
Realm export JSON: VALID (verified with json.tool)
|
||||
File size: 8.9 KB
|
||||
Location: /Users/mastermito/Dev/opencode/infra/keycloak/realm-export.json
|
||||
|
||||
VERIFICATION PROCEDURE
|
||||
----------------------
|
||||
To verify this configuration once Docker is running:
|
||||
|
||||
1. Start Keycloak with realm import:
|
||||
docker compose up -d keycloak
|
||||
|
||||
2. Wait for health check:
|
||||
curl -sf http://localhost:8080/health/ready
|
||||
|
||||
3. Run automated test script:
|
||||
./infra/keycloak/test-auth.sh
|
||||
|
||||
The test script will:
|
||||
- Wait for Keycloak to be ready
|
||||
- Authenticate all 5 test users using password grant
|
||||
- Extract and decode JWT access tokens
|
||||
- Verify 'clubs' claim is present and correctly formatted as JSON object
|
||||
- Validate claim values match expected club memberships
|
||||
- Generate evidence files with decoded JWTs and test results
|
||||
|
||||
EXPECTED JWT STRUCTURE
|
||||
----------------------
|
||||
When admin@test.com authenticates, the JWT should contain:
|
||||
|
||||
{
|
||||
"sub": "<uuid>",
|
||||
"email": "admin@test.com",
|
||||
"email_verified": true,
|
||||
"clubs": {
|
||||
"club-1-uuid": "admin",
|
||||
"club-2-uuid": "member"
|
||||
},
|
||||
"given_name": "Admin",
|
||||
"family_name": "User",
|
||||
...
|
||||
}
|
||||
|
||||
CRITICAL: The 'clubs' claim MUST be a JSON object (not a string).
|
||||
This is controlled by the protocol mapper's jsonType.label: JSON setting.
|
||||
|
||||
DOCKER ENVIRONMENT STATUS
|
||||
--------------------------
|
||||
Docker daemon status: NOT RUNNING (Colima failed to start)
|
||||
Reason: VZ driver error on macOS
|
||||
|
||||
Manual verification steps documented above can be executed when Docker environment is available.
|
||||
The realm export JSON is complete and valid, ready for import.
|
||||
|
||||
ARCHITECTURE IMPACT
|
||||
-------------------
|
||||
This configuration is CRITICAL for multi-tenant architecture:
|
||||
|
||||
1. Backend (Finbuckle) will read 'clubs' claim to:
|
||||
- Identify which tenants (clubs) the user belongs to
|
||||
- Determine user's role within each tenant
|
||||
- Enforce tenant isolation and authorization
|
||||
|
||||
2. Frontend (NextAuth) will use 'clubs' claim to:
|
||||
- Display club switcher UI
|
||||
- Enable user to switch between clubs
|
||||
- Show appropriate UI based on role (admin vs member vs viewer)
|
||||
|
||||
3. Claim format requirements:
|
||||
- MUST be JSON object: {"<tenant-id>": "<role>"}
|
||||
- Key = Club UUID (tenant identifier)
|
||||
- Value = Role string (admin, manager, member, viewer)
|
||||
- If claim is string instead of object, entire auth pipeline breaks
|
||||
|
||||
FILES CREATED
|
||||
-------------
|
||||
- /Users/mastermito/Dev/opencode/infra/keycloak/realm-export.json (realm config)
|
||||
- /Users/mastermito/Dev/opencode/infra/keycloak/test-auth.sh (verification script)
|
||||
- /Users/mastermito/Dev/opencode/.sisyphus/evidence/task-3-user-auth.txt (placeholder)
|
||||
- /Users/mastermito/Dev/opencode/.sisyphus/evidence/task-3-jwt-claims.txt (placeholder)
|
||||
|
||||
NEXT STEPS
|
||||
----------
|
||||
Once Docker environment is running:
|
||||
1. Execute test-auth.sh to verify all users authenticate
|
||||
2. Confirm JWT 'clubs' claim is JSON object (not string)
|
||||
3. Verify claim values match expected roles for each user
|
||||
4. Save JWT samples to evidence files for documentation
|
||||
Reference in New Issue
Block a user