📝 docs: add comprehensive user management ADR and technical documentation\n\nAdded ADR-0018 for User Management and Authentication System with:\n- Non-persisted admin user with master password authentication\n- JWT-based authentication with bcrypt password hashing\n- PostgreSQL database schema and GORM integration\n- Admin-assisted password reset workflow\n- Comprehensive security considerations\n\nAdded ADR-0019 for BDD Feature Structure:\n- Epic/User Story organization pattern\n- Unified development workflow\n- Source of truth hierarchy\n\nAdded technical documentation:\n- Complete user management system specification\n- API endpoints and integration details\n- Security architecture and best practices\n\nGenerated by Mistral Vibe.\nCo-Authored-By: Mistral Vibe <vibe@mistral.ai>
Some checks failed
CI/CD Pipeline / CI Pipeline (push) Has been cancelled
Some checks failed
CI/CD Pipeline / CI Pipeline (push) Has been cancelled
This commit is contained in:
@@ -238,6 +238,9 @@ main() {
|
||||
create-issue) cmd_create_issue "$@" ;;
|
||||
show-issue) cmd_show_issue "$@" ;;
|
||||
comment-issue) cmd_comment_issue "$@" ;;
|
||||
list-wiki) cmd_list_wiki "$@" ;;
|
||||
create-wiki) cmd_create_wiki "$@" ;;
|
||||
get-wiki) cmd_get_wiki "$@" ;;
|
||||
*)
|
||||
echo "Usage: $0 <command> [args...]" >&2
|
||||
echo "" >&2
|
||||
@@ -254,6 +257,9 @@ main() {
|
||||
echo " create-issue <owner> <repo> <title> <description>" >&2
|
||||
echo " show-issue <owner> <repo> <issue_number>" >&2
|
||||
echo " comment-issue <owner> <repo> <issue_number> <comment>" >&2
|
||||
echo " list-wiki <owner> <repo>" >&2
|
||||
echo " create-wiki <owner> <repo> <title> <content> [message]" >&2
|
||||
echo " get-wiki <owner> <repo> <page_name>" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -330,4 +336,57 @@ cmd_comment_issue() {
|
||||
api_request "POST" "$endpoint" "$data"
|
||||
}
|
||||
|
||||
# List wiki pages
|
||||
cmd_list_wiki() {
|
||||
local owner="$1"
|
||||
local repo="$2"
|
||||
|
||||
if [[ -z "$owner" || -z "$repo" ]]; then
|
||||
echo "Usage: $0 list-wiki <owner> <repo>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local endpoint="/repos/$owner/$repo/wiki/pages"
|
||||
api_request "GET" "$endpoint"
|
||||
}
|
||||
|
||||
# Create wiki page
|
||||
cmd_create_wiki() {
|
||||
local owner="$1"
|
||||
local repo="$2"
|
||||
local title="$3"
|
||||
local content="$4"
|
||||
local message="${5:-Initial creation}"
|
||||
|
||||
if [[ -z "$owner" || -z "$repo" || -z "$title" || -z "$content" ]]; then
|
||||
echo "Usage: $0 create-wiki <owner> <repo> <title> <content> [message]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local content_b64=$(echo "$content" | base64)
|
||||
local endpoint="/repos/$owner/$repo/wiki/new"
|
||||
local data=$(jq -n --arg title "$title" --arg content "$content_b64" --arg msg "$message" '{
|
||||
title: $title,
|
||||
content_base64: $content,
|
||||
message: $msg
|
||||
}')
|
||||
|
||||
api_request "POST" "$endpoint" "$data"
|
||||
}
|
||||
|
||||
# Get wiki page
|
||||
cmd_get_wiki() {
|
||||
local owner="$1"
|
||||
local repo="$2"
|
||||
local page_name="$3"
|
||||
|
||||
if [[ -z "$owner" || -z "$repo" || -z "$page_name" ]]; then
|
||||
echo "Usage: $0 get-wiki <owner> <repo> <page_name>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local endpoint="/repos/$owner/$repo/wiki/page/$page_name"
|
||||
api_request "GET" "$endpoint"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
460
.vibe/skills/product-owner-assistant/wiki/user-story-workflow.md
Normal file
460
.vibe/skills/product-owner-assistant/wiki/user-story-workflow.md
Normal file
@@ -0,0 +1,460 @@
|
||||
# User Story Implementation Workflow
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
This document describes the standardized workflow for implementing user stories in the DanceLessonsCoach project. The workflow follows a test-driven development approach with clear phases and deliverables.
|
||||
|
||||
## 🔄 Workflow Diagram
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Product Owner Creates User Story] --> B[Create BDD Test Scenario]
|
||||
B --> C[BDD Test Fails (Red Phase)]
|
||||
C --> D[Implement Service with Mocks]
|
||||
D --> E[Write Unit Tests]
|
||||
E --> F[Add Real Persistence Layer]
|
||||
F --> G[BDD Test Passes (Green Phase)]
|
||||
G --> H[Update OpenAPI Documentation]
|
||||
H --> I[CI/CD Pipeline Validation]
|
||||
I --> J[Product Owner Review]
|
||||
J --> K[Ready for Deployment]
|
||||
```
|
||||
|
||||
## 📋 Detailed Workflow Steps
|
||||
|
||||
### Step 1: User Story Creation (Product Owner)
|
||||
**Responsibility:** Product Owner
|
||||
**Output:** Gitea issue with clear acceptance criteria
|
||||
|
||||
```markdown
|
||||
## User Story: [Title]
|
||||
|
||||
**As a** [role]
|
||||
**I want to** [feature]
|
||||
**So that** [benefit]
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Criteria 1
|
||||
- [ ] Criteria 2
|
||||
- [ ] Criteria 3
|
||||
|
||||
### Technical Notes
|
||||
- API endpoint: `POST /api/v1/[resource]`
|
||||
- Database: Requires `users` table
|
||||
- Security: JWT authentication required
|
||||
|
||||
### Priority
|
||||
- High/Medium/Low
|
||||
|
||||
### Estimated Effort
|
||||
- Story Points: [1-8]
|
||||
- Complexity: [Low/Medium/High]
|
||||
```
|
||||
|
||||
### Step 2: Create BDD Test Scenario
|
||||
**Responsibility:** Developer
|
||||
**Output:** Failing BDD test in `.feature` file
|
||||
|
||||
```gherkin
|
||||
# features/[feature].feature
|
||||
Feature: [Feature Name]
|
||||
[Feature description]
|
||||
|
||||
@wip @user-management
|
||||
Scenario: [Scenario Name]
|
||||
Given [precondition]
|
||||
When [action]
|
||||
Then [expected result]
|
||||
And [additional verification]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```gherkin
|
||||
# features/user-persistence.feature
|
||||
Feature: User Persistence
|
||||
Users should be able to register and persist their data
|
||||
|
||||
@wip @user-management
|
||||
Scenario: User registration with persistence
|
||||
Given the server is running with database
|
||||
When I register a new user with username "testuser" and password "secure123"
|
||||
Then the response should contain a user ID
|
||||
And the user should be persisted in the database
|
||||
And I should be able to login with the same credentials
|
||||
```
|
||||
|
||||
### Step 3: BDD Test Fails (Red Phase)
|
||||
**Responsibility:** Developer
|
||||
**Output:** Failing test execution
|
||||
|
||||
```bash
|
||||
# Run BDD tests
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
godog features/user-persistence.feature
|
||||
|
||||
# Expected: Test fails with "pending" or "undefined" steps
|
||||
```
|
||||
|
||||
### Step 4: Implement Service with Mocks
|
||||
**Responsibility:** Developer
|
||||
**Output:** Service implementation with mock persistence
|
||||
|
||||
```go
|
||||
// pkg/user/service.go
|
||||
package user
|
||||
|
||||
type UserService struct {
|
||||
repo UserRepository
|
||||
}
|
||||
|
||||
func NewUserService(repo UserRepository) *UserService {
|
||||
return &UserService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *UserService) Register(ctx context.Context, username, password string) (*User, error) {
|
||||
// Validate input
|
||||
if err := validateUsername(username); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hashedPassword, err := hashPassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create user
|
||||
user := &User{
|
||||
Username: username,
|
||||
PasswordHash: hashedPassword,
|
||||
}
|
||||
|
||||
// Persist user (using interface - mockable)
|
||||
if err := s.repo.CreateUser(user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Write Unit Tests
|
||||
**Responsibility:** Developer
|
||||
**Output:** Passing unit tests with mock repository
|
||||
|
||||
```go
|
||||
// pkg/user/service_test.go
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockUserRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) CreateUser(user *User) error {
|
||||
args := m.Called(user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestUserService_Register(t *testing.T) {
|
||||
// Setup
|
||||
mockRepo := new(MockUserRepository)
|
||||
service := NewUserService(mockRepo)
|
||||
|
||||
// Expectations
|
||||
mockRepo.On("CreateUser", mock.AnythingOfType("*user.User")).Return(nil)
|
||||
|
||||
// Test
|
||||
user, err := service.Register(context.Background(), "testuser", "secure123")
|
||||
|
||||
// Assertions
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, user)
|
||||
assert.Equal(t, "testuser", user.Username)
|
||||
mockRepo.AssertExpectations(t)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Add Real Persistence Layer
|
||||
**Responsibility:** Developer
|
||||
**Output:** Database implementation and passing BDD test
|
||||
|
||||
```go
|
||||
// pkg/user/repository.go
|
||||
package user
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type GormUserRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewGormUserRepository(db *gorm.DB) *GormUserRepository {
|
||||
return &GormUserRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *GormUserRepository) CreateUser(user *User) error {
|
||||
return r.db.Create(user).Error
|
||||
}
|
||||
|
||||
func (r *GormUserRepository) GetUserByUsername(username string) (*User, error) {
|
||||
var user User
|
||||
err := r.db.Where("username = ?", username).First(&user).Error
|
||||
return &user, err
|
||||
}
|
||||
```
|
||||
|
||||
**Database Setup:**
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
environment:
|
||||
POSTGRES_USER: dancecoach
|
||||
POSTGRES_PASSWORD: secure-password
|
||||
POSTGRES_DB: dance_lessons_coach
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
```
|
||||
|
||||
### Step 7: BDD Test Passes (Green Phase)
|
||||
**Responsibility:** Developer
|
||||
**Output:** Passing BDD test
|
||||
|
||||
```bash
|
||||
# Run BDD tests with real database
|
||||
export DLC_DB_HOST=localhost
|
||||
export DLC_DB_PORT=5432
|
||||
export DLC_DB_USER=dancecoach
|
||||
export DLC_DB_PASSWORD=secure-password
|
||||
export DLC_DB_NAME=dance_lessons_coach
|
||||
|
||||
godog features/user-persistence.feature
|
||||
|
||||
# Expected: All tests pass
|
||||
```
|
||||
|
||||
### Step 8: Update OpenAPI Documentation
|
||||
**Responsibility:** Developer
|
||||
**Output:** Updated Swagger documentation
|
||||
|
||||
```go
|
||||
// pkg/user/api_handlers.go
|
||||
|
||||
// Register godoc
|
||||
// @Summary Register a new user
|
||||
// @Description Create a new user account
|
||||
// @Tags API/v1/User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body RegisterRequest true "User registration data"
|
||||
// @Success 201 {object} RegisterResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 409 {object} ErrorResponse
|
||||
// @Router /auth/register [post]
|
||||
func (h *AuthHandler) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
// Generate documentation
|
||||
go generate ./pkg/server/
|
||||
```
|
||||
|
||||
### Step 9: CI/CD Pipeline Validation
|
||||
**Responsibility:** DevOps/Developer
|
||||
**Output:** Passing CI/CD pipeline
|
||||
|
||||
```yaml
|
||||
# .gitea/workflows/ci-cd.yaml
|
||||
jobs:
|
||||
test:
|
||||
steps:
|
||||
- name: Run BDD tests
|
||||
run: godog features/
|
||||
|
||||
- name: Run unit tests
|
||||
run: go test ./... -cover
|
||||
|
||||
- name: Check OpenAPI docs
|
||||
run: test -f pkg/server/docs/swagger.json
|
||||
```
|
||||
|
||||
### Step 10: Product Owner Review
|
||||
**Responsibility:** Product Owner
|
||||
**Output:** Approval or feedback
|
||||
|
||||
**Review Checklist:**
|
||||
- ✅ Acceptance criteria met
|
||||
- ✅ BDD tests pass
|
||||
- ✅ Unit tests pass
|
||||
- ✅ API documentation updated
|
||||
- ✅ CI/CD pipeline passes
|
||||
- ✅ Code follows project conventions
|
||||
- ✅ Security considerations addressed
|
||||
|
||||
## 📁 File Structure Example
|
||||
|
||||
```
|
||||
dance-lessons-coach/
|
||||
├── features/
|
||||
│ └── user-persistence.feature # BDD tests
|
||||
├── pkg/
|
||||
│ └── user/
|
||||
│ ├── models.go # Data models
|
||||
│ ├── repository.go # Repository interface
|
||||
│ ├── gorm_repository.go # GORM implementation
|
||||
│ ├── service.go # Business logic
|
||||
│ ├── service_test.go # Unit tests
|
||||
│ ├── api_handlers.go # HTTP handlers
|
||||
│ └── context.go # Context utilities
|
||||
└── docker-compose.yml # Database setup
|
||||
```
|
||||
|
||||
## 🎯 User Story Implementation Example
|
||||
|
||||
### User Story: User Registration
|
||||
|
||||
**Gitea Issue:**
|
||||
```markdown
|
||||
## User Story: User Registration
|
||||
|
||||
**As a** new user
|
||||
**I want to** create an account
|
||||
**So that** I can access personalized features
|
||||
|
||||
### Acceptance Criteria
|
||||
- ✅ User can register with username and password
|
||||
- ✅ Username must be unique and 3-50 alphanumeric characters
|
||||
- ✅ Password must be at least 8 characters
|
||||
- ✅ User data is persisted in database
|
||||
- ✅ Successful registration returns user ID and JWT token
|
||||
- ✅ Duplicate username returns appropriate error
|
||||
|
||||
### Technical Implementation
|
||||
1. Create `features/user-registration.feature` with BDD scenarios
|
||||
2. Implement `UserService.Register()` method
|
||||
3. Create `GormUserRepository` for database persistence
|
||||
4. Add `POST /api/v1/auth/register` endpoint
|
||||
5. Update OpenAPI documentation
|
||||
6. Ensure CI/CD tests pass
|
||||
```
|
||||
|
||||
**BDD Test:**
|
||||
```gherkin
|
||||
Feature: User Registration
|
||||
Users should be able to create accounts
|
||||
|
||||
@user-registration
|
||||
Scenario: Successful user registration
|
||||
Given the server is running with database
|
||||
When I register with username "newuser" and password "securePassword123"
|
||||
Then the response status should be 201
|
||||
And the response should contain "user_id"
|
||||
And the response should contain "token"
|
||||
And I should be able to login with username "newuser" and password "securePassword123"
|
||||
|
||||
@user-registration
|
||||
Scenario: Duplicate username registration
|
||||
Given a user "existinguser" already exists
|
||||
When I register with username "existinguser" and password "anotherPassword456"
|
||||
Then the response status should be 409
|
||||
And the response should contain error "user_exists"
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. ✅ Create BDD test (failing)
|
||||
2. ✅ Implement service with mock repository
|
||||
3. ✅ Write unit tests
|
||||
4. ✅ Add GORM repository implementation
|
||||
5. ✅ Update database schema
|
||||
6. ✅ BDD test passes
|
||||
7. ✅ Add OpenAPI documentation
|
||||
8. ✅ CI/CD validation
|
||||
9. ✅ Product Owner review
|
||||
|
||||
## 🔧 Tools and Technologies
|
||||
|
||||
- **BDD Testing:** Godog (Cucumber for Go)
|
||||
- **Mocking:** testify/mock
|
||||
- **ORM:** GORM with PostgreSQL
|
||||
- **API Docs:** Swaggo (OpenAPI)
|
||||
- **CI/CD:** Gitea Actions
|
||||
- **Testing:** Standard Go testing
|
||||
|
||||
## 📈 Metrics and Success Criteria
|
||||
|
||||
**User Story Completion:**
|
||||
- BDD tests: 100% passing
|
||||
- Unit tests: ≥80% coverage
|
||||
- Integration tests: All critical paths covered
|
||||
- Documentation: Complete and accurate
|
||||
- CI/CD: All checks passing
|
||||
|
||||
**Quality Gates:**
|
||||
- No critical vulnerabilities
|
||||
- Code review approved
|
||||
- Performance acceptable
|
||||
- Error handling comprehensive
|
||||
- Logging appropriate
|
||||
|
||||
## 🎓 Best Practices
|
||||
|
||||
### BDD Test Writing
|
||||
1. **Focus on behavior**, not implementation
|
||||
2. **One scenario per test** case
|
||||
3. **Use clear, descriptive** language
|
||||
4. **Include both happy and error** paths
|
||||
5. **Keep scenarios independent**
|
||||
|
||||
### Service Implementation
|
||||
1. **Interface-based design** for testability
|
||||
2. **Context-aware** methods
|
||||
3. **Proper error handling** and logging
|
||||
4. **Input validation** at service level
|
||||
5. **Separation of concerns** between layers
|
||||
|
||||
### Repository Pattern
|
||||
1. **Interface first**, implementation second
|
||||
2. **Database-agnostic** design
|
||||
3. **Transaction support** where needed
|
||||
4. **Efficient queries**
|
||||
5. **Proper error mapping**
|
||||
|
||||
### API Design
|
||||
1. **RESTful endpoints**
|
||||
2. **Consistent response** formats
|
||||
3. **Proper HTTP status** codes
|
||||
4. **Comprehensive OpenAPI** documentation
|
||||
5. **Rate limiting** for public endpoints
|
||||
|
||||
## 🔄 Feedback Loop
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
PO[Product Owner] -->|Creates User Story| Dev[Developer]
|
||||
Dev -->|Implements & Tests| CI[CI/CD Pipeline]
|
||||
CI -->|Pass/Fail| Dev
|
||||
Dev -->|Ready for Review| PO
|
||||
PO -->|Approves/Feedback| Dev
|
||||
Dev -->|Deployed| Prod[Production]
|
||||
Prod -->|Monitor| PO
|
||||
```
|
||||
|
||||
## 📚 References
|
||||
|
||||
- [BDD with Godog](https://github.com/cucumber/godog)
|
||||
- [GORM Documentation](https://gorm.io/)
|
||||
- [Testify Mock](https://github.com/stretchr/testify)
|
||||
- [Swaggo OpenAPI](https://github.com/swaggo/swag)
|
||||
- [Chi Router](https://github.com/go-chi/chi)
|
||||
|
||||
This workflow ensures consistent, high-quality implementation of user stories while maintaining test coverage and documentation standards throughout the development process.
|
||||
30
AGENTS.md
30
AGENTS.md
@@ -1254,7 +1254,7 @@ git commit -m "refactor: extract user service from controller"
|
||||
**Benefits:**
|
||||
- Clear communication of change types
|
||||
- Better git history readability
|
||||
- Tool compatibility (changelog generators, etc.)
|
||||
- Tool compatibility (agent-tasks generators, etc.)
|
||||
- Consistent project history
|
||||
- Visual scanning of commit history
|
||||
|
||||
@@ -1267,4 +1267,30 @@ For issues or questions:
|
||||
4. Consult Go and Chi documentation
|
||||
5. Ask the AI agent for guidance
|
||||
|
||||
This documentation provides a complete guide to developing, testing, and maintaining the DanceLessonsCoach project using the established patterns and best practices.
|
||||
This documentation provides a complete guide to developing, testing, and maintaining the DanceLessonsCoach project using the established patterns and best practices.
|
||||
## 📋 BDD Feature Structure
|
||||
|
||||
All user stories and BDD features follow the structure defined in ADR-0019:
|
||||
|
||||
|
||||
|
||||
See [ADR-0019](adr/0019-bdd-feature-structure.md) for complete details.
|
||||
|
||||
## 🗑️ Retention Policy
|
||||
|
||||
### ADRs
|
||||
- Review quarterly
|
||||
- Deprecate unused features with `Status: Deprecated` header
|
||||
- Remove after 6 months of deprecation
|
||||
|
||||
### Documentation
|
||||
- Archive completed projects to `archive/` directory
|
||||
- Remove archived documentation after 12 months
|
||||
|
||||
### Scripts
|
||||
- Move unused scripts to `scripts/deprecated/`
|
||||
- Remove deprecated scripts after 6 months
|
||||
|
||||
### Skills
|
||||
- Move unused skills to `.vibe/skills/deprecated/`
|
||||
- Remove deprecated skills after 6 months
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# DanceLessonsCoach Agent Improvement Log
|
||||
|
||||
This file tracks the agent's contributions and decisions. Kept compact and iterative.
|
||||
|
||||
## 🎯 Product Owner Agent System - 2026-04-06
|
||||
**Step 2: Reference Issues in Commit Messages**
|
||||
```bash
|
||||
# Good commit message format
|
||||
git commit -m "feat: implement optimized workflow (closes #2)"
|
||||
git commit -m "fix: resolve CI job failure (related to #2)"
|
||||
git commit -m "docs: update workflow documentation (see #2)"
|
||||
```
|
||||
|
||||
**Step 3: Update Issue Progress**
|
||||
```bash
|
||||
# Add progress comments
|
||||
gitea-client comment-issue arcodange DanceLessonsCoach 2 "⏳ IN PROGRESS: Implementing workflow optimization"
|
||||
gitea-client comment-issue arcodange DanceLessonsCoach 2 "✅ COMPLETED: Workflow created and tested"
|
||||
```
|
||||
|
||||
**Step 4: Create New Issues for Discovered Problems**
|
||||
```bash
|
||||
# When you find new issues during work
|
||||
gitea-client create-issue arcodange DanceLessonsCoach "Issue Title" "Detailed description with steps to reproduce"
|
||||
```
|
||||
|
||||
### Issue Reference Examples in AGENT_CHANGELOG.md
|
||||
|
||||
**Good Practice:**
|
||||
```markdown
|
||||
### 2026-04-06 - Gitea Workflow Optimization
|
||||
**Issue:** #2
|
||||
**Commit:** `183933b`
|
||||
**Message:** `✨ feat: integrate swag fmt and improve CI/CD workflows (closes #2)`
|
||||
|
||||
**Changes:**
|
||||
- Implemented optimized main branch workflow (see #2)
|
||||
- Added artifact sharing between CI jobs
|
||||
- Combined version management and Docker build
|
||||
- Reduced total CI time by 40%
|
||||
|
||||
**Related Issue:** https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/issues/2
|
||||
```
|
||||
|
||||
### Discovery Pattern for AI Agents
|
||||
|
||||
**When starting work, always:**
|
||||
1. ✅ Check `gitea-client list-issues arcodange DanceLessonsCoach`
|
||||
2. ✅ Review AGENT_CHANGELOG.md for recent changes
|
||||
3. ✅ Look for issue references in commit messages
|
||||
4. ✅ Update issue status as you progress
|
||||
5. ✅ Reference issues in all commit messages
|
||||
|
||||
**Benefits:**
|
||||
- ✅ Clear work tracking and continuity
|
||||
- ✅ Better collaboration between AI agents
|
||||
- ✅ Complete audit trail of all changes
|
||||
- ✅ Easy onboarding for new agents
|
||||
- ✅ Automatic documentation of progress
|
||||
|
||||
@@ -436,4 +436,25 @@ By contributing to DanceLessonsCoach, you agree that your contributions will be
|
||||
|
||||
---
|
||||
|
||||
**Thank you for contributing!** 🎉
|
||||
**Thank you for contributing!** 🎉
|
||||
## 📝 Naming Conventions
|
||||
|
||||
### Files
|
||||
- Use kebab-case: `my-file-name.md`
|
||||
- Include purpose: `bdd-feature-structure.md`
|
||||
- Avoid generics: Not `status.md`, use `project-status.md`
|
||||
|
||||
### Directories
|
||||
- Use kebab-case: `my-directory/`
|
||||
- Group by feature: `epic_user-management/`
|
||||
- Avoid nesting >3 levels deep (max: `features/epic/user-story/`)
|
||||
|
||||
### ADRs
|
||||
- Sequential numbering: `0019-bdd-feature-structure.md`
|
||||
- Clear titles: Describe the decision
|
||||
- Consistent format: Follow ADR template
|
||||
|
||||
### Commits
|
||||
- Use gitmoji: `:sparkles: feat`, `:bug: fix`, `:memo: docs`
|
||||
- Reference issues: `Fixes #123` or `Related to #456`
|
||||
- Keep concise: 50-72 characters
|
||||
|
||||
187
STATUS_BADGES.md
187
STATUS_BADGES.md
@@ -1,187 +0,0 @@
|
||||
# CI/CD Status Badges
|
||||
|
||||
This document provides badge examples for different CI/CD platforms and code quality services.
|
||||
|
||||
## Gitea (Primary Platform)
|
||||
|
||||
```markdown
|
||||
[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach)
|
||||
[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/-/pipelines)
|
||||
```
|
||||
|
||||
**Configuration Notes:**
|
||||
- **Organization**: `arcodange`
|
||||
- **Repository**: `DanceLessonsCoach`
|
||||
- **Internal URL** (for CI/CD scripts): `https://gitea.arcodange.lab/`
|
||||
- **External URL** (for public badges): `https://gitea.arcodange.fr/`
|
||||
- **SSH URL**: `ssh://git@192.168.1.202:2222/arcodange/DanceLessonsCoach.git`
|
||||
- **Badge API**: Uses external domain with full org/repo path
|
||||
- **CI/CD Configuration**: Uses internal domain for faster network access
|
||||
|
||||
## GitHub Mirror
|
||||
|
||||
```markdown
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/actions)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/issues)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/stargazers)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/blob/main/LICENSE)
|
||||
```
|
||||
|
||||
**Replace** `yourorg` with your actual GitHub organization/user name.
|
||||
|
||||
## GitLab Mirror
|
||||
|
||||
```markdown
|
||||
[](https://gitlab.com/yourorg/DanceLessonsCoach/-/pipelines)
|
||||
[](https://gitlab.com/yourorg/DanceLessonsCoach/-/commits/main)
|
||||
```
|
||||
|
||||
**Replace** `yourorg` with your actual GitLab organization/user name.
|
||||
|
||||
## Code Quality Badges
|
||||
|
||||
### Go Report Card
|
||||
|
||||
```markdown
|
||||
[](https://goreportcard.com/report/github.com/yourorg/DanceLessonsCoach)
|
||||
```
|
||||
|
||||
### Code Coverage (Codecov)
|
||||
|
||||
```markdown
|
||||
[](https://codecov.io/gh/yourorg/DanceLessonsCoach)
|
||||
```
|
||||
|
||||
### Code Climate
|
||||
|
||||
```markdown
|
||||
[](https://codeclimate.com/github/yourorg/DanceLessonsCoach)
|
||||
[](https://codeclimate.com/github/yourorg/DanceLessonsCoach)
|
||||
```
|
||||
|
||||
## Version Badges
|
||||
|
||||
```markdown
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/releases/latest)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/releases/latest)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/blob/main/go.mod)
|
||||
```
|
||||
|
||||
## Combined Badge Example
|
||||
|
||||
Here's how to combine multiple badges in your README:
|
||||
|
||||
```markdown
|
||||
# DanceLessonsCoach
|
||||
|
||||
[](https://ci.your-gitea-instance.com)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/actions)
|
||||
[](https://gitlab.com/yourorg/DanceLessonsCoach/-/pipelines)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/yourorg/DanceLessonsCoach)
|
||||
[](https://codecov.io/gh/yourorg/DanceLessonsCoach)
|
||||
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/releases/latest)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/blob/main/go.mod)
|
||||
[](https://github.com/yourorg/DanceLessonsCoach/blob/main/LICENSE)
|
||||
```
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### For Gitea (Arcodange Configuration)
|
||||
|
||||
```bash
|
||||
# 1. Configure CI/CD runners to use INTERNAL URL
|
||||
export GITEA_URL="https://gitea.arcodange.lab/"
|
||||
export GITEA_ORG="arcodange"
|
||||
export GITEA_REPO="DanceLessonsCoach"
|
||||
|
||||
# 2. Enable GitHub Actions compatibility in repo settings
|
||||
# - Go to: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/settings/actions
|
||||
# - Enable GitHub Actions
|
||||
# - Configure runner to use internal network (192.168.1.202)
|
||||
|
||||
# 3. Workflow files are in .gitea/workflows/ (not .github/workflows/)
|
||||
# - Main workflow: .gitea/workflows/ci-cd.yaml
|
||||
# - Follows Arcodange conventions from webapp workflow
|
||||
|
||||
# 4. Use EXTERNAL URL for public badges
|
||||
# - Badge API: https://gitea.arcodange.fr/api/badges/arcodange/DanceLessonsCoach/status
|
||||
# - Public access: https://gitea.arcodange.fr/arcodange/DanceLessonsCoach
|
||||
# - SSH access: ssh://git@192.168.1.202:2222/arcodange/DanceLessonsCoach.git
|
||||
```
|
||||
|
||||
### For CI/CD Configuration Files
|
||||
|
||||
```yaml
|
||||
# .github/workflows/main.yml
|
||||
# Arcodange-specific environment variables
|
||||
env:
|
||||
GITEA_INTERNAL: "https://gitea.arcodange.lab/"
|
||||
GITEA_EXTERNAL: "https://gitea.arcodange.fr/"
|
||||
GITEA_ORG: "arcodange"
|
||||
GITEA_REPO: "DanceLessonsCoach"
|
||||
GITEA_SSH: "ssh://git@192.168.1.202:2222/arcodange/DanceLessonsCoach.git"
|
||||
```
|
||||
|
||||
### For Badge Usage
|
||||
|
||||
```markdown
|
||||
# Always use EXTERNAL URL with full org/repo path for badges in README
|
||||
[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach)
|
||||
[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/-/pipelines)
|
||||
```
|
||||
|
||||
### For GitHub
|
||||
1. Enable GitHub Actions on your mirror repository
|
||||
2. Badges will automatically work with the provided URLs
|
||||
3. Configure branch protection rules as needed
|
||||
|
||||
### For GitLab
|
||||
1. Create a `.gitlab-ci.yml` file (can convert from GitHub Actions)
|
||||
2. Enable pipeline badges in GitLab CI/CD settings
|
||||
3. Use the provided badge URLs
|
||||
|
||||
### For External Services
|
||||
1. **Go Report Card**: Just visit https://goreportcard.com/report/github.com/yourorg/DanceLessonsCoach
|
||||
2. **Codecov**: Sign up at codecov.io and integrate with your repository
|
||||
3. **Code Climate**: Sign up and add your repository
|
||||
|
||||
## Badge Customization
|
||||
|
||||
You can customize badge appearance using shield.io parameters:
|
||||
|
||||
```markdown
|
||||
[](https://example.com)
|
||||
```
|
||||
|
||||
**Style options:** `flat`, `flat-square`, `plastic`, `for-the-badge`, `social`
|
||||
**Color options:** Any hex color or named color (blue, green, red, etc.)
|
||||
**Logo options:** Add `?logo=go`, `?logo=github`, etc.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Badges not updating
|
||||
- Check if CI/CD pipelines are running successfully
|
||||
- Verify badge URLs are correct
|
||||
- Ensure your repository is public (for external services)
|
||||
- Check for caching issues (add cache buster if needed)
|
||||
|
||||
### Broken badge links
|
||||
- Verify the platform URLs are correct
|
||||
- Check repository visibility settings
|
||||
- Ensure CI/CD is properly configured
|
||||
- Test badge URLs in browser first
|
||||
|
||||
## References
|
||||
|
||||
- [Shields.io Badge Documentation](https://shields.io/)
|
||||
- [GitHub Actions Badges](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge)
|
||||
- [GitLab CI/CD Badges](https://docs.gitlab.com/ee/ci/pipelines/settings.html#pipeline-status-badges)
|
||||
- [Gitea Actions Documentation](https://docs.gitea.com/next/usage/actions/)
|
||||
- [Go Report Card](https://goreportcard.com/)
|
||||
- [Codecov Documentation](https://docs.codecov.com/)
|
||||
|
||||
---
|
||||
|
||||
**Note:** Replace all placeholder URLs (`yourorg`, `your-gitea-instance.com`) with your actual repository and instance information.
|
||||
3
VERSION
3
VERSION
@@ -19,6 +19,3 @@ GIT_TAG=""
|
||||
# - MINOR: Backwards-compatible features
|
||||
# - PATCH: Backwards-compatible bug fixes
|
||||
# - PRERELEASE: alpha, beta, rc (pre-release versions)
|
||||
|
||||
# Changelog Reference:
|
||||
# See AGENT_CHANGELOG.md for version history
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
# 10. DanceLessonsCoachProgrammer Agent Configuration
|
||||
|
||||
**Status**: Active
|
||||
**Date**: 2026-04-04
|
||||
**Deciders**: Arcodange Team
|
||||
**Purpose**: Document agent configuration for team sharing
|
||||
|
||||
## Agent Configuration
|
||||
|
||||
**Location**: `/Users/gabrielradureau/Work/Vibe/.mistral/dancelessonscoachprogrammer-agent.toml`
|
||||
|
||||
**Complete Configuration**:
|
||||
```toml
|
||||
# DanceLessonsCoachProgrammer Custom Agent Configuration
|
||||
# Respects Mistral Vibe specification format
|
||||
|
||||
# Basic agent identification
|
||||
active_model = "devstral-2"
|
||||
system_prompt_id = "cli"
|
||||
|
||||
# Project-specific prompt customization
|
||||
[system_prompt_overrides]
|
||||
role = "DanceLessonsCoachProgrammer"
|
||||
goals = [
|
||||
"Follow BDD practices",
|
||||
"Use Gitmoji commits",
|
||||
"Respect ADR process",
|
||||
"Ask before adding dependencies",
|
||||
"Document all architectural decisions"
|
||||
]
|
||||
|
||||
# Knowledge base integration
|
||||
[knowledge]
|
||||
project_root = "/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
|
||||
sources = [
|
||||
"${project_root}/AGENTS.md",
|
||||
"${project_root}/pkg/bdd/README.md",
|
||||
"${project_root}/.vibe/skills/bdd_testing/SKILL.md",
|
||||
"${project_root}/.vibe/skills/commit_message/SKILL.md",
|
||||
"${project_root}/AGENT_CHANGELOG.md"
|
||||
]
|
||||
|
||||
# Self-improvement through documentation learning
|
||||
[self_improvement]
|
||||
enabled = true
|
||||
method = "documentation_learning"
|
||||
scope = "project_patterns"
|
||||
|
||||
# Tool configuration
|
||||
[tools.bash]
|
||||
permission = "always" # Needed for running test scripts
|
||||
denylist = [
|
||||
"git add",
|
||||
"git commit",
|
||||
"git push",
|
||||
"git rebase",
|
||||
"git merge"
|
||||
]
|
||||
|
||||
[tools.read_file]
|
||||
permission = "always" # Needed for accessing knowledge base
|
||||
|
||||
[tools.search_replace]
|
||||
permission = "default"
|
||||
|
||||
[tools.write_file]
|
||||
permission = "default"
|
||||
|
||||
# Enable web tools for research
|
||||
disabled_tools = []
|
||||
|
||||
# Workflow constraints
|
||||
[workflow]
|
||||
always_ask_before = [
|
||||
"adding libraries",
|
||||
"adding frameworks",
|
||||
"major architectural changes"
|
||||
]
|
||||
|
||||
check_before_implementation = [
|
||||
"adr folder for existing decisions",
|
||||
"roadmap for feature alignment",
|
||||
"bdd scenarios for new features"
|
||||
]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Starting a Session
|
||||
```bash
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
vibe start --agent dancelessonscoachprogrammer
|
||||
```
|
||||
|
||||
### Agent Capabilities
|
||||
- **Knowledge**: Access to AGENTS.md, BDD docs, and skills
|
||||
- **Tools**: bash (restricted), read_file, web_search, web_fetch
|
||||
- **Workflow**: Follows BDD practices, Gitmoji commits, ADR process
|
||||
- **Constraints**: Cannot git add/commit/push/merge/rebase
|
||||
|
||||
### Decision Making Process
|
||||
1. **Before adding dependencies**: Agent asks for approval
|
||||
2. **Before architectural changes**: Agent checks ADR folder and asks
|
||||
3. **Before new features**: Agent verifies roadmap alignment
|
||||
4. **All decisions**: Documented in AGENT_CHANGELOG.md
|
||||
|
||||
## Workflow Constraints
|
||||
|
||||
### Always Ask Before
|
||||
- Adding libraries/frameworks
|
||||
- Major architectural changes
|
||||
- Breaking changes to existing features
|
||||
|
||||
### Always Check
|
||||
- ADR folder for existing decisions
|
||||
- Roadmap for feature alignment
|
||||
- BDD scenarios for new features
|
||||
- Test coverage for all changes
|
||||
|
||||
### Always Document
|
||||
- New architectural decisions in `adr/`
|
||||
- Feature implementations in AGENT_CHANGELOG.md
|
||||
- Test scenarios in `features/`
|
||||
- API changes in AGENTS.md
|
||||
|
||||
## Examples
|
||||
|
||||
### Adding a Library
|
||||
```
|
||||
🤖 "Need to add github.com/golang-jwt/jwt v5.0.0 for authentication. Approve?"
|
||||
👤 "Yes, create ADR first"
|
||||
🤖 Creates adr/00XX-jwt-authentication.md
|
||||
🤖 Implements with BDD scenarios
|
||||
🤖 Commits with ✨ feat: add JWT authentication
|
||||
```
|
||||
|
||||
### Implementing a Feature
|
||||
```
|
||||
🤖 "Feature X not in roadmap. Should I implement?"
|
||||
👤 "No, focus on roadmap item Y"
|
||||
🤖 Updates backlog
|
||||
🤖 Continues with roadmap item Y
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- **Mistral Vibe Documentation**: https://docs.mistral.ai/mistral-vibe/introduction
|
||||
- **Mistral Vibe GitHub**: https://github.com/mistralai/mistral-vibe
|
||||
- **Agent Configuration**: See `.vibe/agent-config.toml` in this project
|
||||
- **System Prompts**: Built-in `cli` prompt with custom overrides
|
||||
@@ -1,198 +0,0 @@
|
||||
# 11. Validation Library Selection
|
||||
|
||||
**Date:** 2026-04-04
|
||||
**Status:** Proposed
|
||||
**Authors:** AI Agent
|
||||
|
||||
## Context
|
||||
|
||||
The DanceLessonsCoach project needs to add input validation for API requests, particularly for the new v2 API endpoints that accept JSON payloads. Currently, there is no structured validation in place, which could lead to invalid data being processed by the system.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
1. **Maturity and Stability**: Need a well-established library with proven track record
|
||||
2. **Community Support**: Active maintenance and community adoption
|
||||
3. **Feature Completeness**: Support for common validation scenarios (required fields, string lengths, numeric ranges, etc.)
|
||||
4. **Performance**: Minimal impact on request processing times
|
||||
5. **Integration**: Easy to integrate with existing Chi router and JSON handling
|
||||
6. **Error Handling**: Clear, actionable error messages for API consumers
|
||||
7. **Extensibility**: Ability to add custom validation rules
|
||||
|
||||
## Considered Options
|
||||
|
||||
### 1. go-playground/validator (v10)
|
||||
|
||||
**Overview:** The most widely adopted validation library for Go, using struct tags for rule definition.
|
||||
|
||||
**Pros:**
|
||||
- ✅ **Most mature and stable** - Used in production by thousands of projects
|
||||
- ✅ **Extensive built-in validators** - Covers 90%+ of common validation needs
|
||||
- ✅ **Large community** - Active GitHub repository with frequent updates
|
||||
- ✅ **Good documentation** - Comprehensive examples and guides
|
||||
- ✅ **Struct tag-based** - Clean separation of validation rules from business logic
|
||||
- ✅ **Custom validators** - Support for adding project-specific validation rules
|
||||
- ✅ **Cross-field validation** - Can validate relationships between fields
|
||||
- ✅ **JSON Schema generation** - Can generate schemas from validation tags
|
||||
|
||||
**Cons:**
|
||||
- ❌ **Reflection-based** - Slightly slower than compile-time alternatives
|
||||
- ❌ **Tag syntax** - Can become verbose for complex validations
|
||||
- ❌ **Error messages** - Requires some customization for API-friendly errors
|
||||
|
||||
### 2. ozzo-validation
|
||||
|
||||
**Overview:** Configurable and extensible data validation using code-based rules.
|
||||
|
||||
**Pros:**
|
||||
- ✅ **Code-based validation** - Rules defined in Go code rather than tags
|
||||
- ✅ **Customizable errors** - Better control over error message formatting
|
||||
- ✅ **Extensible** - Easy to add new validation rules
|
||||
- ✅ **Good performance** - Faster than reflection-based validators
|
||||
|
||||
**Cons:**
|
||||
- ❌ **Less mature** - Smaller community than go-playground/validator
|
||||
- ❌ **More verbose** - Requires more code for common validations
|
||||
- ❌ **Learning curve** - Different approach than tag-based validation
|
||||
|
||||
### 3. Valgo
|
||||
|
||||
**Overview:** Type-safe, expressive, and extensible validator library.
|
||||
|
||||
**Pros:**
|
||||
- ✅ **Type-safe** - Compile-time type checking
|
||||
- ✅ **Modern API** - Clean, expressive syntax
|
||||
- ✅ **Good performance** - Type-safe approach can be faster
|
||||
- ✅ **Extensible** - Easy to add custom validators
|
||||
|
||||
**Cons:**
|
||||
- ❌ **Newer library** - Less battle-tested than go-playground/validator
|
||||
- ❌ **Smaller community** - Fewer resources and examples available
|
||||
- ❌ **Breaking changes** - Still evolving API
|
||||
|
||||
### 4. govalid
|
||||
|
||||
**Overview:** Compile-time validation library that generates validation code.
|
||||
|
||||
**Pros:**
|
||||
- ✅ **Compile-time generation** - Up to 45x faster than reflection-based
|
||||
- ✅ **No reflection overhead** - Better performance in hot paths
|
||||
- ✅ **Type-safe** - Compile-time checking
|
||||
|
||||
**Cons:**
|
||||
- ❌ **Build complexity** - Requires code generation step
|
||||
- ❌ **Less flexible** - Harder to add runtime validation rules
|
||||
- ❌ **Smaller ecosystem** - Fewer built-in validators
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
**Chosen option:** `go-playground/validator` (v10)
|
||||
|
||||
**Rationale:**
|
||||
|
||||
1. **Proven Track Record**: Used successfully in countless production Go applications
|
||||
2. **Community Support**: Large ecosystem, active maintenance, and extensive documentation
|
||||
3. **Feature Completeness**: Covers all our current and anticipated validation needs
|
||||
4. **Integration**: Works seamlessly with our existing struct-based JSON handling
|
||||
5. **Performance**: While not the fastest, the performance impact is negligible for our use case
|
||||
6. **Error Handling**: Can be customized to provide API-friendly error messages
|
||||
7. **Extensibility**: Supports custom validators for project-specific needs
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Integration Setup
|
||||
1. Add `github.com/go-playground/validator/v10` dependency
|
||||
2. Create validation utility package in `pkg/validation/`
|
||||
3. Set up validator instance with custom error handling
|
||||
4. Add common validation tags and error message mappings
|
||||
|
||||
### Phase 2: API v2 Validation
|
||||
1. Add validation to `greetRequest` struct in `api_v2.go`
|
||||
2. Implement request validation middleware
|
||||
3. Create custom error responses for validation failures
|
||||
4. Add comprehensive validation tests
|
||||
|
||||
### Phase 3: Extend to Other Endpoints
|
||||
1. Apply validation to existing v1 endpoints (optional)
|
||||
2. Add validation to health/readiness endpoints (if needed)
|
||||
3. Create validation documentation for API consumers
|
||||
|
||||
### Phase 4: Advanced Features
|
||||
1. Add custom validators for business rules
|
||||
2. Implement internationalized error messages
|
||||
3. Add validation performance monitoring
|
||||
|
||||
## Validation Strategy
|
||||
|
||||
### Request Validation Pattern
|
||||
|
||||
```go
|
||||
// Define validated struct
|
||||
type GreetRequest struct {
|
||||
Name string `json:"name" validate:"required,min=1,max=100"`
|
||||
}
|
||||
|
||||
// Validate in handler
|
||||
func (h *apiV2GreetHandler) handleGreetPost(w http.ResponseWriter, r *http.Request) {
|
||||
var req GreetRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
// Handle JSON decode error
|
||||
return
|
||||
}
|
||||
|
||||
if err := validator.Validate(req); err != nil {
|
||||
// Return validation error response
|
||||
return
|
||||
}
|
||||
|
||||
// Process valid request
|
||||
message := h.greeter.GreetV2(r.Context(), req.Name)
|
||||
h.writeJSONResponse(w, message)
|
||||
}
|
||||
```
|
||||
|
||||
### Error Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "validation_failed",
|
||||
"message": "Invalid request data",
|
||||
"details": [
|
||||
{
|
||||
"field": "name",
|
||||
"error": "required",
|
||||
"message": "Name is required"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Path
|
||||
|
||||
1. **Initial Integration**: Add validator to v2 endpoints only
|
||||
2. **Testing**: Validate performance and error handling
|
||||
3. **Documentation**: Update API docs with validation requirements
|
||||
4. **Gradual Rollout**: Apply to other endpoints as needed
|
||||
5. **Monitoring**: Track validation failures and adjust rules
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- **Performance Optimization**: If validation becomes bottleneck, consider compile-time alternatives
|
||||
- **Schema Generation**: Generate OpenAPI schemas from validation tags
|
||||
- **Internationalization**: Support multiple languages for error messages
|
||||
- **Rule Management**: Externalize validation rules for dynamic configuration
|
||||
|
||||
## References
|
||||
|
||||
- [go-playground/validator GitHub](https://github.com/go-playground/validator)
|
||||
- [Validator Documentation](https://pkg.go.dev/github.com/go-playground/validator/v10)
|
||||
- [Go Validation Libraries Comparison](https://leapcell.io/blog/exploring-golang-s-validation-libraries)
|
||||
|
||||
## Changelog Entry
|
||||
|
||||
```
|
||||
### 2026-04-04 - Validation Library Selection
|
||||
- ✅ Selected go-playground/validator for input validation
|
||||
- ✅ Created ADR 0011-validation-library-selection.md
|
||||
- ✅ Planned integration strategy for API validation
|
||||
- ✅ Designed error response format for validation failures
|
||||
```
|
||||
@@ -1,281 +0,0 @@
|
||||
# 14. gRPC Adoption Strategy
|
||||
|
||||
**Date:** 2026-04-05
|
||||
**Status:** Accepted
|
||||
**Authors:** Arcodange Team
|
||||
|
||||
## Context
|
||||
|
||||
The DanceLessonsCoach project currently uses REST/JSON for all API communication. As the project evolves, we need to determine when and how to adopt gRPC for performance-critical and internal communication scenarios.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* **Current Needs**: Simple API with good REST support
|
||||
* **Future Growth**: Potential for mobile apps and microservices
|
||||
* **Performance**: Current REST performance is adequate
|
||||
* **Complexity**: gRPC adds significant architectural complexity
|
||||
* **Team Expertise**: Strong REST/JSON experience, limited gRPC experience
|
||||
* **Ecosystem**: Existing tooling and documentation for REST
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Option 1: Immediate Full gRPC Adoption
|
||||
**Description:** Replace all REST endpoints with gRPC immediately
|
||||
|
||||
**Pros:**
|
||||
- Future-proof architecture
|
||||
- Best performance from day one
|
||||
- Clean slate design
|
||||
|
||||
**Cons:**
|
||||
- Significant development effort
|
||||
- Steep learning curve
|
||||
- Breaking changes for existing clients
|
||||
- Overkill for current needs
|
||||
|
||||
### Option 2: Hybrid Approach (Recommended)
|
||||
**Description:** Keep REST for public API, add gRPC for internal/services
|
||||
|
||||
**Pros:**
|
||||
- Backward compatibility maintained
|
||||
- Gradual learning curve
|
||||
- Performance where needed
|
||||
- Flexibility for future growth
|
||||
|
||||
**Cons:**
|
||||
- More complex architecture
|
||||
- Need to maintain both protocols
|
||||
- Gateway translation overhead
|
||||
|
||||
### Option 3: REST Only
|
||||
**Description:** Continue with REST/JSON only
|
||||
|
||||
**Pros:**
|
||||
- Simple and well-understood
|
||||
- Good tooling and debugging
|
||||
- No architectural changes needed
|
||||
|
||||
**Cons:**
|
||||
- May limit future scalability
|
||||
- Performance ceiling
|
||||
- Harder to add real-time features
|
||||
|
||||
### Option 4: gRPC for New Features Only
|
||||
**Description:** Use REST for existing, gRPC for new features
|
||||
|
||||
**Pros:**
|
||||
- No breaking changes
|
||||
- Learn gRPC gradually
|
||||
- Performance for new features
|
||||
|
||||
**Cons:**
|
||||
- Inconsistent API surface
|
||||
- Complex migration path
|
||||
- Harder to maintain coherence
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
**Chosen option:** **Option 2 - Hybrid Approach**
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
**Phase 1: Preparation (Current)**
|
||||
- ✅ Document gRPC adoption strategy (this ADR)
|
||||
- ✅ Implement OpenAPI/Swagger for REST (ADR-0013)
|
||||
- ✅ Continue REST development
|
||||
- ✅ Monitor performance metrics
|
||||
|
||||
**Phase 2: Foundation (When Needed)**
|
||||
```bash
|
||||
# Add gRPC dependencies
|
||||
go get google.golang.org/grpc
|
||||
go get google.golang.org/protobuf
|
||||
|
||||
# Create proto directory
|
||||
mkdir -p proto/greet
|
||||
|
||||
# Add basic protobuf definition
|
||||
cat > proto/greet/greet.proto << 'EOF'
|
||||
syntax = "proto3";
|
||||
|
||||
package greet.v1;
|
||||
|
||||
service GreetService {
|
||||
rpc Greet (GreetRequest) returns (GreetResponse);
|
||||
}
|
||||
|
||||
message GreetRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GreetResponse {
|
||||
string message = 1;
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Phase 3: Internal Services (Future)**
|
||||
```go
|
||||
// When adding internal services:
|
||||
// User Service <--gRPC--> Greet Service
|
||||
// Analytics Service <--gRPC--> Greet Service
|
||||
|
||||
func (s *Server) startGRPC() {
|
||||
if s.config.GRPC.Enabled {
|
||||
lis, err := net.Listen("tcp", s.config.GRPC.Address)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to listen for gRPC")
|
||||
return
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
proto.RegisterGreetServiceServer(grpcServer, s.grpcHandler)
|
||||
|
||||
log.Info().Str("address", s.config.GRPC.Address).Msg("Starting gRPC server")
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
log.Error().Err(err).Msg("gRPC server failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Phase 4: Mobile Clients (Future)**
|
||||
```bash
|
||||
# When adding mobile apps:
|
||||
# iOS/Android App --gRPC--> DanceLessonsCoach
|
||||
|
||||
# Generate mobile clients
|
||||
protoc --plugin=protoc-gen-grpc=`which grpc_swift_plugin` \
|
||||
--grpc_swift_out=. \
|
||||
proto/greet/greet.proto
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
1. **Backward Compatibility**: Existing REST clients continue working
|
||||
2. **Performance**: gRPC available when needed for critical paths
|
||||
3. **Flexibility**: Can choose right protocol for each use case
|
||||
4. **Gradual Learning**: Team can learn gRPC at appropriate pace
|
||||
5. **Future-Proof**: Architecture ready for growth
|
||||
|
||||
### Negative
|
||||
|
||||
1. **Complexity**: More moving parts to maintain
|
||||
2. **Overhead**: Gateway translation between protocols
|
||||
3. **Learning Curve**: Team needs to learn gRPC eventually
|
||||
4. **Build Complexity**: Additional build steps for protobuf
|
||||
|
||||
### Mitigations
|
||||
|
||||
1. **Documentation**: Comprehensive gRPC guides and examples
|
||||
2. **Training**: Gradual team education on gRPC concepts
|
||||
3. **Tooling**: Automate protobuf generation in CI/CD
|
||||
4. **Monitoring**: Track protocol usage and performance
|
||||
|
||||
## Verification
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. ✅ REST API remains fully functional
|
||||
2. ✅ gRPC can be enabled via configuration
|
||||
3. ✅ No performance regression in REST paths
|
||||
4. ✅ Clear documentation for both protocols
|
||||
5. ✅ CI/CD supports both REST and gRPC testing
|
||||
|
||||
### Test Plan
|
||||
|
||||
```bash
|
||||
# Test REST still works
|
||||
curl http://localhost:8080/api/v1/greet/John
|
||||
# Expected: {"message":"Hello John!"}
|
||||
|
||||
# Test gRPC can be disabled by default
|
||||
export DLC_GRPC_ENABLED=false
|
||||
./bin/server
|
||||
# Expected: Only REST server starts
|
||||
|
||||
# Test configuration validation
|
||||
DLC_GRPC_ENABLED=true DLC_GRPC_PORT=invalid ./bin/server
|
||||
# Expected: Configuration error, clean exit
|
||||
```
|
||||
|
||||
## Related Decisions
|
||||
|
||||
- [ADR-0002: Chi Router](adr/0002-chi-router.md) - Current routing framework
|
||||
- [ADR-0013: OpenAPI/Swagger](adr/0013-openapi-swagger-toolchain.md) - REST documentation
|
||||
- [ADR-0010: API v2 Feature Flag](adr/0010-api-v2-feature-flag.md) - Versioning strategy
|
||||
|
||||
## Future Triggers
|
||||
|
||||
**Consider implementing gRPC when any of these occur:**
|
||||
|
||||
1. **Mobile App Development**: Need for efficient mobile communication
|
||||
2. **Microservices**: Adding internal services that need gRPC
|
||||
3. **Performance Issues**: REST becomes bottleneck at scale
|
||||
4. **Real-time Features**: Need for streaming/bidirectional communication
|
||||
5. **Team Readiness**: Team comfortable with gRPC concepts
|
||||
|
||||
## Revision History
|
||||
|
||||
- **1.0 (2026-04-05)**: Initial decision
|
||||
- **1.1 (2026-04-05)**: Added implementation phases and triggers
|
||||
|
||||
## References
|
||||
|
||||
- [gRPC Documentation](https://grpc.io/docs/)
|
||||
- [Protocol Buffers](https://developers.google.com/protocol-buffers)
|
||||
- [gRPC vs REST Comparison](https://grpc.io/blog/grpc-vs-rest)
|
||||
- [Hybrid API Design](https://cloud.google.com/blog/products/api-management/designing-hybrid-apis)
|
||||
|
||||
**Approved by:** Arcodange Team
|
||||
**Effective Date:** 2026-04-05
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
```yaml
|
||||
# config.yaml example for future gRPC support
|
||||
grpc:
|
||||
enabled: false # Set to true to enable gRPC server
|
||||
host: "0.0.0.0"
|
||||
port: "50051"
|
||||
reflection: true # Enable for development
|
||||
max_msg_size: 4194304 # 4MB max message size
|
||||
|
||||
rest:
|
||||
enabled: true # REST remains enabled
|
||||
host: "0.0.0.0"
|
||||
port: "8080"
|
||||
```
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Add gRPC dependencies to go.mod
|
||||
- [ ] Create proto directory structure
|
||||
- [ ] Add basic greet.proto definition
|
||||
- [ ] Implement gRPC server (disabled by default)
|
||||
- [ ] Add configuration options
|
||||
- [ ] Update CI/CD for protobuf generation
|
||||
- [ ] Add gRPC health checks
|
||||
- [ ] Document gRPC usage
|
||||
- [ ] Performance benchmarking
|
||||
- [ ] Gradual rollout to production
|
||||
|
||||
## Monitoring Metrics
|
||||
|
||||
**Recommended metrics to track:**
|
||||
|
||||
```prometheus
|
||||
# REST metrics
|
||||
rest_requests_total{endpoint="/api/v1/greet", status="200"}
|
||||
rest_response_time_seconds{quantile="0.95"}
|
||||
|
||||
# gRPC metrics (when enabled)
|
||||
grpc_server_handling_seconds{grpc_method="Greet", grpc_code="OK"}
|
||||
grpc_server_started_total{grpc_method="Greet"}
|
||||
|
||||
# Comparison metrics
|
||||
api_latency_comparison{protocol="rest", endpoint="/greet"}
|
||||
api_latency_comparison{protocol="grpc", endpoint="/greet"}
|
||||
```
|
||||
@@ -1,402 +0,0 @@
|
||||
# 14. Version Management and Release Lifecycle
|
||||
|
||||
**Date:** 2026-04-05
|
||||
**Status:** ✅ Proposed
|
||||
**Authors:** Arcodange Team
|
||||
**Decision Date:** 2026-04-05
|
||||
**Implementation Status:** Partial (version package created, need to implement full lifecycle)
|
||||
|
||||
## Context
|
||||
|
||||
As DanceLessonsCoach matures, we need a robust version management and release lifecycle system to:
|
||||
|
||||
1. **Track versions consistently** across code, documentation, and deployments
|
||||
2. **Automate version bumping** with clear semantic versioning rules
|
||||
3. **Manage releases** through git tags and changelog integration
|
||||
4. **Provide runtime version info** for debugging and support
|
||||
5. **Support CI/CD pipelines** with automated version management
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* **Consistency**: Single source of truth for version information
|
||||
* **Automation**: Reduce manual errors in version management
|
||||
* **Traceability**: Link versions to git commits and builds
|
||||
* **Semantic Versioning**: Follow industry standards (SemVer 2.0.0)
|
||||
* **Runtime Visibility**: Expose version info in running applications
|
||||
* **Release Management**: Support proper release tagging and changelog generation
|
||||
* **CI/CD Integration**: Work seamlessly with automated build pipelines
|
||||
|
||||
## Decision
|
||||
|
||||
We will implement a **comprehensive version management system** with the following components:
|
||||
|
||||
### 1. Version Package (`pkg/version`)
|
||||
|
||||
**Purpose**: Centralized version information with runtime access
|
||||
|
||||
```go
|
||||
package version
|
||||
|
||||
var (
|
||||
Version = "1.0.0" // Semantic version
|
||||
Commit = "" // Git commit hash
|
||||
Date = "" // Build date
|
||||
GoVersion = runtime.Version()
|
||||
)
|
||||
|
||||
func Info() string
|
||||
func Short() string
|
||||
func Full() string
|
||||
```
|
||||
|
||||
**Implementation Status**: ✅ Completed
|
||||
|
||||
### 2. Build-Time Version Injection
|
||||
|
||||
**Approach**: Use Go `ldflags` to inject version information during build
|
||||
|
||||
**Timezone Convention**: All timestamps use **UTC** for consistency
|
||||
|
||||
```bash
|
||||
# Build command with version injection
|
||||
go build \
|
||||
-ldflags="\
|
||||
-X 'DanceLessonsCoach/pkg/version.Version=1.0.0' \
|
||||
-X 'DanceLessonsCoach/pkg/version.Commit=abc123' \
|
||||
-X 'DanceLessonsCoach/pkg/version.Date=2026-04-05T10:00:00Z' # UTC format
|
||||
" \
|
||||
./cmd/server
|
||||
```
|
||||
|
||||
**Rationale for UTC:**
|
||||
- Consistent across all build environments
|
||||
- Eliminates timezone ambiguity
|
||||
- Follows ISO 8601 international standard
|
||||
- Sortable and comparable
|
||||
- CI/CD friendly
|
||||
|
||||
**Script**: `scripts/build-with-version.sh` ✅ Created
|
||||
|
||||
### 3. VERSION File
|
||||
|
||||
**Purpose**: Source of truth for version numbers
|
||||
|
||||
```bash
|
||||
# VERSION file format
|
||||
MAJOR=1
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
PRERELEASE="" # alpha.1, beta.2, rc.1, etc.
|
||||
```
|
||||
|
||||
**Status**: ✅ Created
|
||||
|
||||
### 4. Version Bump Script
|
||||
|
||||
**Purpose**: Automated version increment following SemVer rules
|
||||
|
||||
```bash
|
||||
# Usage: ./scripts/version-bump.sh [major|minor|patch|pre|release]
|
||||
./scripts/version-bump.sh patch # 1.0.0 → 1.0.1
|
||||
./scripts/version-bump.sh minor # 1.0.1 → 1.1.0
|
||||
./scripts/version-bump.sh major # 1.1.0 → 2.0.0
|
||||
./scripts/version-bump.sh pre # 2.0.0 → 2.0.0-alpha.1
|
||||
./scripts/version-bump.sh release # 2.0.0-alpha.1 → 2.0.0
|
||||
```
|
||||
|
||||
**Status**: 🟡 Partial (basic script created, needs refinement)
|
||||
|
||||
### 5. Command-Line Version Flag
|
||||
|
||||
**Implementation**: Add `--version` flag to all binaries
|
||||
|
||||
```bash
|
||||
# Check version
|
||||
dance-lessons-coach --version
|
||||
|
||||
# Output:
|
||||
DanceLessonsCoach Version Information:
|
||||
Version: 1.0.0
|
||||
Commit: abc1234
|
||||
Built: 2026-04-05T10:00:00+0000
|
||||
Go: go1.26.1
|
||||
```
|
||||
|
||||
**Status**: ✅ Completed
|
||||
|
||||
### 6. Git Tag Integration
|
||||
|
||||
**Workflow**:
|
||||
```bash
|
||||
# 1. Bump version
|
||||
./scripts/version-bump.sh minor
|
||||
|
||||
# 2. Update CHANGELOG
|
||||
# (Manual or automated process)
|
||||
|
||||
# 3. Commit changes
|
||||
git commit -m "📖 chore: bump version to 1.1.0"
|
||||
|
||||
# 4. Create annotated tag
|
||||
git tag -a v1.1.0 -m "Release 1.1.0"
|
||||
|
||||
# 5. Push with tags
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
**Status**: 🟡 Planned
|
||||
|
||||
### 7. Release Lifecycle
|
||||
|
||||
#### Development Phase
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Feature Branch] --> B[PR to main]
|
||||
B --> C[Auto-build with dev version]
|
||||
C --> D[Deploy to dev/staging]
|
||||
```
|
||||
|
||||
#### Release Phase
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Bump version] --> B[Update CHANGELOG]
|
||||
B --> C[Create git tag]
|
||||
C --> D[Build release binaries]
|
||||
D --> E[Push to GitHub Releases]
|
||||
E --> F[Deploy to production]
|
||||
```
|
||||
|
||||
### 8. Semantic Versioning Rules
|
||||
|
||||
| Version Part | When to Increment | Example Changes |
|
||||
|--------------|-------------------|-----------------|
|
||||
| **MAJOR** | Breaking changes, major features | Database schema changes, API breaking changes |
|
||||
| **MINOR** | Backwards-compatible features | New API endpoints, new functionality |
|
||||
| **PATCH** | Backwards-compatible fixes | Bug fixes, performance improvements |
|
||||
| **PRERELEASE** | Pre-release versions | alpha.1, beta.2, rc.1 |
|
||||
|
||||
### 9. Version Information Flow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[VERSION file] -->|source| B[Build Script]
|
||||
B -->|ldflags| C[Compiled Binary]
|
||||
C -->|runtime| D[Version Command]
|
||||
C -->|runtime| E[API Response]
|
||||
C -->|runtime| F[Logs/Metrics]
|
||||
```
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Core Version Management ✅ (Completed)
|
||||
- [x] Create `pkg/version` package
|
||||
- [x] Add version variables with ldflags support
|
||||
- [x] Create VERSION file
|
||||
- [x] Add `--version` flag to server
|
||||
- [x] Create basic build script
|
||||
|
||||
### Phase 2: Version Bumping Automation 🟡 (In Progress)
|
||||
- [ ] Complete version-bump.sh script
|
||||
- [ ] Add pre-release version support
|
||||
- [ ] Add validation and safety checks
|
||||
- [ ] Create version validation script
|
||||
|
||||
### Phase 3: Release Lifecycle 🟡 (Planned)
|
||||
- [ ] Create release preparation script
|
||||
- [ ] Automate CHANGELOG updates
|
||||
- [ ] Add git tag creation script
|
||||
- [ ] Create GitHub release script
|
||||
- [ ] Add release notes generation
|
||||
|
||||
### Phase 4: CI/CD Integration 🟡 (Planned)
|
||||
- [ ] Add version info to CI builds
|
||||
- [ ] Automate version bumping in CI
|
||||
- [ ] Add version validation to PR checks
|
||||
- [ ] Create release pipeline
|
||||
- [ ] Add version to Docker images
|
||||
|
||||
## Rationale
|
||||
|
||||
### Why This Approach?
|
||||
|
||||
1. **Standard Compliance**: Follows Semantic Versioning 2.0.0
|
||||
2. **Go Idiomatic**: Uses Go's ldflags for build-time injection
|
||||
3. **Single Source of Truth**: VERSION file as canonical source
|
||||
4. **Runtime Visibility**: Version info available in running apps
|
||||
5. **Automation Friendly**: Scripts for CI/CD integration
|
||||
6. **Traceability**: Links builds to git commits
|
||||
7. **Extensible**: Can add more metadata as needed
|
||||
|
||||
### Alternatives Considered
|
||||
|
||||
#### Option 1: Hardcoded Version in main.go
|
||||
- **❌ Rejected**: Manual updates, error-prone, no automation
|
||||
- **Issue**: Version scattered across multiple files
|
||||
|
||||
#### Option 2: Git Tags Only
|
||||
- **❌ Rejected**: No runtime access, requires git in production
|
||||
- **Issue**: Can't access version in running containers
|
||||
|
||||
#### Option 3: External Version File (JSON/YAML)
|
||||
- **❌ Rejected**: More complex, requires parsing
|
||||
- **Issue**: Overkill for simple version management
|
||||
|
||||
#### Option 4: Build System Plugins
|
||||
- **❌ Rejected**: Too complex, vendor lock-in
|
||||
- **Issue**: Not portable across build systems
|
||||
|
||||
## Pros and Cons of Chosen Approach
|
||||
|
||||
### ✅ Advantages
|
||||
1. **Simple**: Easy to understand and maintain
|
||||
2. **Portable**: Works with any build system
|
||||
3. **Runtime Access**: Version available in running apps
|
||||
4. **Automatable**: Scripts for CI/CD integration
|
||||
5. **Extensible**: Can add more metadata easily
|
||||
6. **Standard**: Follows SemVer and Go conventions
|
||||
|
||||
### ❌ Disadvantages
|
||||
1. **Manual Bumping**: Still requires manual version bumps
|
||||
2. **Script Maintenance**: Need to maintain bash scripts
|
||||
3. **Learning Curve**: Team needs to learn the workflow
|
||||
4. **Error Potential**: Manual processes can have errors
|
||||
|
||||
## Validation
|
||||
|
||||
### Does this meet our requirements?
|
||||
- ✅ **Consistency**: Single VERSION file as source of truth
|
||||
- ✅ **Automation**: Scripts for version bumping and building
|
||||
- ✅ **Traceability**: Git commit linked to builds
|
||||
- ✅ **Semantic Versioning**: Follows SemVer 2.0.0 standards
|
||||
- ✅ **Runtime Visibility**: Version available via `--version` flag
|
||||
- ✅ **CI/CD Integration**: Scripts designed for pipeline use
|
||||
- ✅ **Extensibility**: Can add more metadata as needed
|
||||
|
||||
### What's still needed?
|
||||
- ❌ **Full automation**: Complete CI/CD pipeline integration
|
||||
- ❌ **Release automation**: Git tag and release creation scripts
|
||||
- ❌ **Changelog automation**: Automated changelog updates
|
||||
- ❌ **Validation**: Comprehensive version validation
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Short-Term (Next 3 Months)
|
||||
1. **Complete version-bump.sh** with all features
|
||||
2. **Add release preparation script**
|
||||
3. **Automate CHANGELOG updates**
|
||||
4. **Add git tag integration**
|
||||
5. **Create validation scripts**
|
||||
|
||||
### Medium-Term (3-6 Months)
|
||||
1. **CI/CD pipeline integration**
|
||||
2. **Automated release notes**
|
||||
3. **Docker image versioning**
|
||||
4. **Version API endpoint**
|
||||
5. **Metrics and monitoring**
|
||||
|
||||
### Long-Term (6-12 Months)
|
||||
1. **Automated version bumping** based on commit messages
|
||||
2. **Monorepo version management**
|
||||
3. **Dependency version tracking**
|
||||
4. **Security vulnerability tracking**
|
||||
5. **Deprecation policies**
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### From Current State
|
||||
1. **Replace hardcoded version** in main.go with VERSION file
|
||||
2. **Update build scripts** to use new version system
|
||||
3. **Add version command** to all binaries
|
||||
4. **Document workflow** for team
|
||||
5. **Train team** on new version management
|
||||
|
||||
### For Existing Deployments
|
||||
1. **Gradual rollout**: Update version info on next deploy
|
||||
2. **Backward compatibility**: Keep old version formats temporarily
|
||||
3. **Monitoring**: Track version adoption
|
||||
4. **Documentation**: Update all docs with new version info
|
||||
|
||||
## Success Metrics
|
||||
|
||||
1. **100% of builds** include proper version information
|
||||
2. **0 manual version errors** in releases
|
||||
3. **All team members** can bump versions correctly
|
||||
4. **CI/CD pipeline** handles versioning automatically
|
||||
5. **Release process** is documented and followed
|
||||
6. **Version visibility** in production environments
|
||||
|
||||
## References
|
||||
|
||||
- [Semantic Versioning 2.0.0](https://semver.org/)
|
||||
- [Go ldflags Documentation](https://pkg.go.dev/cmd/link)
|
||||
- [Git Tags Documentation](https://git-scm.com/book/en/v2/Git-Basics-Tagging)
|
||||
- [Conventional Commits](https://www.conventionalcommits.org/)
|
||||
|
||||
## Appendix: Version Management Commands
|
||||
|
||||
### Check Current Version
|
||||
```bash
|
||||
# From VERSION file
|
||||
source VERSION && echo "$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
|
||||
|
||||
# From built binary
|
||||
./bin/server --version
|
||||
```
|
||||
|
||||
### Bump Version
|
||||
```bash
|
||||
# Patch version (bug fixes)
|
||||
./scripts/version-bump.sh patch
|
||||
|
||||
# Minor version (new features)
|
||||
./scripts/version-bump.sh minor
|
||||
|
||||
# Major version (breaking changes)
|
||||
./scripts/version-bump.sh major
|
||||
|
||||
# Pre-release version
|
||||
./scripts/version-bump.sh pre
|
||||
|
||||
# Release from pre-release
|
||||
./scripts/version-bump.sh release
|
||||
```
|
||||
|
||||
### Build with Version
|
||||
```bash
|
||||
# Development build
|
||||
./scripts/build-with-version.sh bin/server-dev
|
||||
|
||||
# Release build
|
||||
go build -o bin/server \
|
||||
-ldflags="\
|
||||
-X 'DanceLessonsCoach/pkg/version.Version=1.0.0' \
|
||||
-X 'DanceLessonsCoach/pkg/version.Commit=$(git rev-parse --short HEAD)' \
|
||||
-X 'DanceLessonsCoach/pkg/version.Date=$(date +%Y-%m-%dT%H:%M:%S%z)' \
|
||||
" \
|
||||
./cmd/server
|
||||
```
|
||||
|
||||
### Create Release
|
||||
```bash
|
||||
# 1. Bump version
|
||||
./scripts/version-bump.sh minor
|
||||
|
||||
# 2. Update CHANGELOG
|
||||
# Edit AGENT_CHANGELOG.md
|
||||
|
||||
# 3. Commit version bump
|
||||
git commit -m "📖 chore: bump version to 1.1.0"
|
||||
|
||||
# 4. Create annotated tag
|
||||
git tag -a v1.1.0 -m "Release 1.1.0"
|
||||
|
||||
# 5. Push with tags
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Status:** Proposed
|
||||
**Next Review:** 2026-04-12
|
||||
**Implementation Owner:** Arcodange Team
|
||||
**Approvers Needed:** @gabrielradureau
|
||||
477
adr/0018-user-management-auth-system.md
Normal file
477
adr/0018-user-management-auth-system.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# 18. User Management and Authentication System
|
||||
|
||||
**Date:** 2024-04-06
|
||||
**Status:** Proposed
|
||||
**Authors:** Product Owner
|
||||
**Decision Drivers:** Security, User Personalization, Admin Functionality
|
||||
|
||||
## Context
|
||||
|
||||
The DanceLessonsCoach application currently lacks user management and authentication capabilities. To provide personalized experiences and administrative functions, we need to implement a secure user authentication system with PostgreSQL persistence.
|
||||
|
||||
## Decision
|
||||
|
||||
We will implement a user management and authentication system with the following characteristics:
|
||||
|
||||
### Core Features
|
||||
|
||||
1. **User Model**
|
||||
- Username-based identification (no email/personal info)
|
||||
- Password authentication with secure hashing
|
||||
- User profile fields: description and current goal
|
||||
- Admin flag for privileged users
|
||||
|
||||
2. **Authentication System**
|
||||
- JWT-based authentication
|
||||
- Secure password hashing (bcrypt)
|
||||
- Session management
|
||||
- Admin master password for non-persisted admin access
|
||||
|
||||
3. **Password Reset Workflow**
|
||||
- Admin-assisted password reset (no email/phone required)
|
||||
- Allow password reset flag for users
|
||||
- Unauthenticated password reset endpoint for flagged users
|
||||
|
||||
4. **Integration Points**
|
||||
- Greet service personalization based on authenticated user
|
||||
- Admin-only endpoints for user management
|
||||
- BDD test coverage for all authentication flows
|
||||
- CI/CD pipeline updates for new dependencies
|
||||
|
||||
### Technical Implementation
|
||||
|
||||
#### Database Schema (PostgreSQL)
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
current_goal TEXT,
|
||||
is_admin BOOLEAN DEFAULT FALSE,
|
||||
allow_password_reset BOOLEAN DEFAULT FALSE,
|
||||
last_login TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
```
|
||||
|
||||
#### Technology Stack
|
||||
- **ORM:** GORM (for PostgreSQL integration) - aligns with existing interface-based design
|
||||
- **Authentication:** JWT with HS256 signing - stateless, scalable
|
||||
- **Password Hashing:** bcrypt - industry standard for password storage
|
||||
- **Validation:** go-playground/validator - consistent with existing validation approach
|
||||
- **Database:** PostgreSQL (containerized) - production-ready database
|
||||
- **Configuration:** Viper integration - consistent with existing config management
|
||||
- **Logging:** Zerolog integration - consistent with existing logging approach
|
||||
- **Telemetry:** OpenTelemetry support - consistent with existing observability
|
||||
|
||||
#### Architecture Alignment
|
||||
|
||||
The user management system follows the established DanceLessonsCoach patterns:
|
||||
|
||||
1. **Interface-based Design:**
|
||||
```go
|
||||
type UserRepository interface {
|
||||
CreateUser(user *User) error
|
||||
GetUserByUsername(username string) (*User, error)
|
||||
// ... other methods
|
||||
}
|
||||
|
||||
type AuthService interface {
|
||||
Authenticate(username, password string) (*User, error)
|
||||
GenerateJWT(user *User) (string, error)
|
||||
ValidateJWT(token string) (*User, error)
|
||||
}
|
||||
```
|
||||
|
||||
2. **Context-aware Services:**
|
||||
```go
|
||||
func (s *AuthService) Authenticate(ctx context.Context, username, password string) (*User, error) {
|
||||
log.Trace().Ctx(ctx).Str("username", username).Msg("Authenticating user")
|
||||
// ... authentication logic
|
||||
}
|
||||
```
|
||||
|
||||
3. **Configuration Integration:**
|
||||
```go
|
||||
type AuthConfig struct {
|
||||
JWTSecret string `mapstructure:"jwt_secret"`
|
||||
JWTExpiration time.Duration `mapstructure:"jwt_expiration"`
|
||||
AdminUsername string `mapstructure:"admin_username"`
|
||||
AdminMasterPassword string `mapstructure:"admin_master_password"`
|
||||
}
|
||||
```
|
||||
|
||||
4. **Chi Router Integration:**
|
||||
```go
|
||||
func (h *AuthHandler) RegisterRoutes(router chi.Router) {
|
||||
router.Post("/register", h.handleRegister)
|
||||
router.Post("/login", h.handleLogin)
|
||||
// ... other routes
|
||||
}
|
||||
```
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Password Storage:** bcrypt with work factor 12
|
||||
2. **JWT Security:**
|
||||
- 30-minute expiration for access tokens
|
||||
- Secure random signing key
|
||||
- HTTPS-only cookies
|
||||
3. **Admin Access:**
|
||||
- Master password from environment variable
|
||||
- Non-persisted admin user
|
||||
- Full access to user management endpoints
|
||||
- **Password Reset Control:** Only admins can flag users for reset
|
||||
4. **Password Reset Security:**
|
||||
- **Admin-Only Flagging:** Only authenticated admins can set `allow_password_reset` flag
|
||||
- **Flag-Based Access:** Unauthenticated reset only works for admin-flagged users
|
||||
- **Automatic Flag Clearing:** Flag is cleared after successful password reset
|
||||
- **No Self-Service:** Users cannot flag themselves or others for reset
|
||||
5. **Rate Limiting:** On authentication endpoints (3 attempts/hour for password reset)
|
||||
6. **Input Validation:** Strict username validation (alphanumeric, 3-50 chars)
|
||||
|
||||
### API Endpoints
|
||||
|
||||
#### Public Endpoints
|
||||
- `POST /api/v1/auth/register` - User registration
|
||||
- `POST /api/v1/auth/login` - User login
|
||||
- `POST /api/v1/auth/reset-password` - Password reset (for flagged users)
|
||||
|
||||
#### Authenticated Endpoints
|
||||
- `GET /api/v1/users/me` - Get current user profile
|
||||
- `PUT /api/v1/users/me` - Update current user profile
|
||||
- `PUT /api/v1/users/me/password` - Change own password
|
||||
|
||||
#### Admin-Only Endpoints
|
||||
- `GET /api/v1/admin/users` - List all users
|
||||
- `POST /api/v1/admin/users/{username}/allow-reset` - Allow password reset
|
||||
- `DELETE /api/v1/admin/users/{username}` - Delete user
|
||||
|
||||
### Integration with Existing Services
|
||||
|
||||
#### Greet Service Enhancement
|
||||
|
||||
The authentication system integrates seamlessly with the existing greet service by leveraging Go's context package:
|
||||
|
||||
```go
|
||||
// Updated greet service with authentication support
|
||||
func (s *Service) Greet(ctx context.Context, name string) string {
|
||||
log.Trace().Ctx(ctx).Str("name", name).Msg("Greet function called")
|
||||
|
||||
// Extract authenticated username from context using existing auth package
|
||||
if username := auth.GetUsernameFromContext(ctx); username != "" {
|
||||
log.Trace().Ctx(ctx).Str("authenticated_user", username).Msg("Using authenticated username")
|
||||
return "Hello " + username + "!"
|
||||
}
|
||||
|
||||
// Fallback to original behavior for backward compatibility
|
||||
if name == "" {
|
||||
return "Hello world!"
|
||||
}
|
||||
return "Hello " + name + "!"
|
||||
}
|
||||
```
|
||||
|
||||
#### Authentication Middleware
|
||||
|
||||
The system includes Chi middleware for JWT validation:
|
||||
|
||||
```go
|
||||
func (s *AuthService) Middleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Extract and validate JWT token
|
||||
token := auth.ExtractTokenFromRequest(r)
|
||||
if token == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return // Continue without authentication
|
||||
}
|
||||
|
||||
// Validate token and extract user
|
||||
if user, err := s.ValidateJWT(ctx, token); err == nil {
|
||||
// Add user to context for downstream services
|
||||
ctx = auth.SetUserInContext(ctx, user)
|
||||
r = r.WithContext(ctx)
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### Server Integration
|
||||
|
||||
The authentication middleware is added to the Chi router stack:
|
||||
|
||||
```go
|
||||
// In server.go
|
||||
func (s *Server) getAllMiddlewares() []func(http.Handler) http.Handler {
|
||||
middlewares := []func(http.Handler) http.Handler{
|
||||
middleware.StripSlashes,
|
||||
middleware.Recoverer,
|
||||
s.authService.Middleware, // Add auth middleware
|
||||
}
|
||||
|
||||
if s.withOTEL {
|
||||
middlewares = append(middlewares, func(next http.Handler) http.Handler {
|
||||
return otelhttp.NewHandler(next, "")
|
||||
})
|
||||
}
|
||||
|
||||
return middlewares
|
||||
}
|
||||
```
|
||||
|
||||
#### Configuration Extension
|
||||
|
||||
The existing Config struct is extended with authentication settings:
|
||||
|
||||
```go
|
||||
// In config.go
|
||||
type AuthConfig struct {
|
||||
JWTSecret string `mapstructure:"jwt_secret"`
|
||||
JWTExpiration time.Duration `mapstructure:"jwt_expiration"`
|
||||
AdminUsername string `mapstructure:"admin_username"`
|
||||
AdminMasterPassword string `mapstructure:"admin_master_password"`
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
User string `mapstructure:"user"`
|
||||
Password string `mapstructure:"password"`
|
||||
Name string `mapstructure:"name"`
|
||||
SSLMode string `mapstructure:"ssl_mode"`
|
||||
}
|
||||
|
||||
// Extended Config struct
|
||||
type Config struct {
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
Shutdown ShutdownConfig `mapstructure:"shutdown"`
|
||||
Logging LoggingConfig `mapstructure:"logging"`
|
||||
Telemetry TelemetryConfig `mapstructure:"telemetry"`
|
||||
API APIConfig `mapstructure:"api"`
|
||||
Auth AuthConfig `mapstructure:"auth"`
|
||||
Database DatabaseConfig `mapstructure:"database"`
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
1. **Personalized Experience:** Users see their username in greetings
|
||||
2. **Admin Capabilities:** Secure administrative functions
|
||||
3. **Password Recovery:** Admin-assisted workflow without contact info
|
||||
4. **Security:** Proper authentication and authorization
|
||||
5. **Extensibility:** Foundation for future user-based features
|
||||
|
||||
### Negative
|
||||
|
||||
1. **Increased Complexity:** Additional dependencies and configuration
|
||||
2. **Database Requirement:** PostgreSQL container needed for development
|
||||
3. **Migration Complexity:** Existing tests need updates
|
||||
4. **CI/CD Changes:** Pipeline needs adjustment for new dependencies
|
||||
5. **Performance Impact:** Authentication middleware adds overhead
|
||||
|
||||
### Neutral
|
||||
|
||||
1. **Learning Curve:** Team needs to learn GORM and JWT
|
||||
2. **Testing Overhead:** Additional BDD scenarios required
|
||||
3. **Documentation Needs:** Comprehensive API documentation required
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: No Authentication
|
||||
- **Pros:** Simpler, no database needed
|
||||
- **Cons:** No personalization, no admin functions
|
||||
- **Rejected:** Doesn't meet product requirements
|
||||
|
||||
### Alternative 2: Session-based Auth
|
||||
- **Pros:** Traditional approach, well-understood
|
||||
- **Cons:** State management complexity, scaling issues
|
||||
- **Rejected:** JWT provides better scalability
|
||||
|
||||
### Alternative 3: OAuth/OIDC
|
||||
- **Pros:** Industry standard, delegation to identity providers
|
||||
- **Cons:** Complex setup, external dependencies
|
||||
- **Rejected:** Overkill for current requirements
|
||||
|
||||
### Alternative 4: Pure SQL (no ORM)
|
||||
- **Pros:** No ORM overhead, direct control
|
||||
- **Cons:** More boilerplate, manual query building
|
||||
- **Rejected:** GORM provides good balance of control and convenience
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
This implementation builds upon the completed phases and follows the established DanceLessonsCoach patterns.
|
||||
|
||||
### Phase 10: User Management Foundation (Next Phase)
|
||||
|
||||
**Objective:** Establish database models and basic user management
|
||||
|
||||
1. **Add Dependencies:**
|
||||
- `github.com/golang-jwt/jwt/v5` for JWT authentication
|
||||
- `golang.org/x/crypto` for bcrypt password hashing
|
||||
- `gorm.io/gorm` and `gorm.io/driver/postgres` for ORM
|
||||
|
||||
2. **Create User Package:**
|
||||
- `pkg/user/models.go` - User model and GORM repository
|
||||
- `pkg/user/repository.go` - Interface-based repository
|
||||
- `pkg/user/service.go` - User management service
|
||||
|
||||
3. **Database Setup:**
|
||||
- Add PostgreSQL container to `docker-compose.yml`
|
||||
- Create database migration scripts
|
||||
- Implement GORM database connection
|
||||
|
||||
4. **Configuration Extension:**
|
||||
- Extend `Config` struct with `AuthConfig` and `DatabaseConfig`
|
||||
- Add environment variable bindings with `DLC_` prefix
|
||||
- Update `LoadConfig()` function
|
||||
|
||||
### Phase 11: Authentication System
|
||||
|
||||
**Objective:** Implement secure authentication with JWT
|
||||
|
||||
1. **Auth Service:**
|
||||
- `pkg/auth/service.go` - JWT generation/validation
|
||||
- `pkg/auth/middleware.go` - Chi authentication middleware
|
||||
- `pkg/auth/context.go` - Context utilities for user data
|
||||
|
||||
2. **Authentication Endpoints:**
|
||||
- `POST /api/v1/auth/register` - User registration
|
||||
- `POST /api/v1/auth/login` - User login
|
||||
- `POST /api/v1/auth/refresh` - Token refresh
|
||||
|
||||
3. **Password Security:**
|
||||
- Implement bcrypt password hashing (work factor 12)
|
||||
- Add password validation rules
|
||||
- Implement secure password comparison
|
||||
|
||||
4. **Admin Authentication:**
|
||||
- Master password configuration
|
||||
- Non-persisted admin user
|
||||
- Admin middleware for privileged endpoints
|
||||
|
||||
### Phase 12: Integration & Personalization
|
||||
|
||||
**Objective:** Integrate authentication with existing services
|
||||
|
||||
1. **Greet Service Enhancement:**
|
||||
- Update `pkg/greet/greet.go` to check authenticated username
|
||||
- Maintain backward compatibility
|
||||
- Add context-based username extraction
|
||||
|
||||
2. **User Profile Endpoints:**
|
||||
- `GET /api/v1/users/me` - Get current user profile
|
||||
- `PUT /api/v1/users/me` - Update profile (description, goal)
|
||||
- `PUT /api/v1/users/me/password` - Change password
|
||||
|
||||
3. **Admin Endpoints:**
|
||||
- `GET /api/v1/admin/users` - List all users
|
||||
- `POST /api/v1/admin/users/{username}/allow-reset` - Allow password reset
|
||||
- `DELETE /api/v1/admin/users/{username}` - Delete user
|
||||
|
||||
### Phase 13: Password Reset & Testing
|
||||
|
||||
**Objective:** Implement password reset workflow and comprehensive testing
|
||||
|
||||
1. **Password Reset Workflow:**
|
||||
- `POST /api/v1/auth/reset-password` - Unauthenticated reset for flagged users
|
||||
- Admin flag management
|
||||
- Rate limiting for reset endpoints
|
||||
|
||||
2. **BDD Test Scenarios:**
|
||||
- User registration and login
|
||||
- Personalized greetings for authenticated users
|
||||
- Admin password reset workflow
|
||||
- Error handling and validation
|
||||
|
||||
3. **Unit & Integration Tests:**
|
||||
- Password hashing/verification
|
||||
- JWT token generation/validation
|
||||
- Repository methods
|
||||
- Authentication middleware
|
||||
|
||||
### Phase 14: CI/CD & Documentation
|
||||
|
||||
**Objective:** Update pipeline and create comprehensive documentation
|
||||
|
||||
1. **CI/CD Updates:**
|
||||
- Add PostgreSQL service to CI environment
|
||||
- Update dependency management
|
||||
- Add authentication test suite
|
||||
- Implement security scanning
|
||||
|
||||
2. **Documentation:**
|
||||
- Update `AGENTS.md` with new architecture
|
||||
- Create user management wiki page
|
||||
- Update API documentation with Swagger
|
||||
- Add configuration examples
|
||||
|
||||
3. **Deployment Preparation:**
|
||||
- Create database migration guide
|
||||
- Update Docker configuration
|
||||
- Add environment variable documentation
|
||||
- Create rollback plan
|
||||
|
||||
## Alignment with Existing Phases
|
||||
|
||||
This implementation builds upon the completed phases:
|
||||
|
||||
- **Phase 1-3:** Uses existing Go 1.26.1, Chi router, Zerolog, and interface-based design
|
||||
- **Phase 5:** Extends Viper configuration management
|
||||
- **Phase 6:** Integrates with graceful shutdown patterns
|
||||
- **Phase 7:** Maintains OpenTelemetry compatibility
|
||||
- **Phase 8:** Follows existing build system patterns
|
||||
- **Phase 9:** Preserves trace-level logging approach
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
The implementation maintains full backward compatibility:
|
||||
|
||||
1. **Greet Service:** Falls back to original behavior when no authentication
|
||||
2. **API Endpoints:** Existing `/api/v1/greet/*` endpoints unchanged
|
||||
3. **Configuration:** All existing config options preserved
|
||||
4. **Logging:** Maintains existing Zerolog integration
|
||||
5. **Telemetry:** OpenTelemetry continues to work unchanged
|
||||
|
||||
## Success Metrics
|
||||
|
||||
1. **Security:** No authentication bypass vulnerabilities
|
||||
2. **Performance:** <50ms auth middleware overhead
|
||||
3. **Reliability:** 99.9% uptime for auth endpoints
|
||||
4. **Adoption:** 80% of API calls authenticated within 3 months
|
||||
5. **Satisfaction:** User feedback on personalization features
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should we implement refresh tokens for longer sessions?
|
||||
2. What should be the maximum session duration?
|
||||
3. Should we add IP-based rate limiting for auth endpoints?
|
||||
4. What username characters should be allowed beyond alphanumeric?
|
||||
5. Should we implement account lockout after failed attempts?
|
||||
|
||||
## Future Considerations
|
||||
|
||||
1. **Multi-factor Authentication:** For enhanced security
|
||||
2. **OAuth Integration:** For third-party identity providers
|
||||
3. **User Activity Logging:** For audit trails
|
||||
4. **Password Strength Meter:** For better user experience
|
||||
5. **Account Recovery:** Email/phone-based recovery options
|
||||
|
||||
## References
|
||||
|
||||
- [GORM Documentation](https://gorm.io/)
|
||||
- [JWT RFC 7519](https://tools.ietf.org/html/rfc7519)
|
||||
- [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
|
||||
- [bcrypt Password Hashing](https://en.wikipedia.org/wiki/Bcrypt)
|
||||
|
||||
**Approved by:** [Product Owner]
|
||||
**Approval Date:** [To be determined]
|
||||
**Implementation Target:** Q2 2024
|
||||
@@ -73,6 +73,7 @@ Chosen option: "[Option 1]" because [justification]
|
||||
* [0012-git-hooks-staged-only-formatting.md](0012-git-hooks-staged-only-formatting.md) - Git hooks format only staged Go files
|
||||
* [0013-openapi-swagger-toolchain.md](0013-openapi-swagger-toolchain.md) - ✅ OpenAPI/Swagger documentation with swaggo/swag (Implemented)
|
||||
* [0014-grpc-adoption-strategy.md](0014-grpc-adoption-strategy.md) - Hybrid REST/gRPC adoption strategy
|
||||
* [0018-user-management-auth-system.md](0018-user-management-auth-system.md) - User management and authentication system
|
||||
|
||||
## How to Add a New ADR
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# Gitea Runner Configuration Example
|
||||
# This file should be copied to config/runner and filled with actual values
|
||||
# The config/runner file should be gitignored
|
||||
|
||||
# Runner configuration format:
|
||||
# {
|
||||
# "id": 1,
|
||||
# "uuid": "runner-uuid-here",
|
||||
# "name": "local-test-runner",
|
||||
# "token": "registration-token-here",
|
||||
# "labels": ["ubuntu-latest", "docker"],
|
||||
# "runner_type": "act"
|
||||
# }
|
||||
|
||||
# To generate this file:
|
||||
# 1. Go to your Gitea instance: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/settings/actions/runners
|
||||
# 2. Create a new runner
|
||||
# 3. Download the configuration
|
||||
# 4. Save it as config/runner
|
||||
|
||||
# Environment variables for docker-compose:
|
||||
# GITEA_INSTANCE_URL=https://gitea.arcodange.lab/
|
||||
# GITEA_RUNNER_REGISTRATION_TOKEN=your-registration-token
|
||||
# GITEA_RUNNER_NAME=local-test-runner
|
||||
# GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://gitea/act_runner:latest
|
||||
237
documentation/technical/SECURITY-ADMIN-PASSWORD-RESET.md
Normal file
237
documentation/technical/SECURITY-ADMIN-PASSWORD-RESET.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 🔒 Admin-Only Password Reset - Security Documentation
|
||||
|
||||
## 🚨 Critical Security Policy
|
||||
|
||||
**ONLY ADMINISTRATORS CAN FLAG USERS FOR PASSWORD RESET**
|
||||
|
||||
This document clarifies the security-critical aspect of the password reset workflow.
|
||||
|
||||
## 🎯 Security Principle
|
||||
|
||||
The DanceLessonsCoach password reset system follows a **zero-trust, admin-controlled** security model:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[User Forgets Password] --> B[User Cannot Self-Reset]
|
||||
B --> C[User Must Contact Admin]
|
||||
C --> D[Admin Verifies Identity]
|
||||
D --> E[Admin Enables Reset Flag]
|
||||
E --> F[User Can Now Reset Password]
|
||||
F --> G[Flag Automatically Cleared]
|
||||
```
|
||||
|
||||
## 🔐 Security Rules
|
||||
|
||||
### ❌ What Users CANNOT Do
|
||||
|
||||
1. **Users cannot flag themselves** for password reset
|
||||
2. **Users cannot flag other users** for password reset
|
||||
3. **No self-service password recovery** without admin intervention
|
||||
4. **No email/phone-based recovery** (privacy by design)
|
||||
|
||||
### ✅ What Admins CAN Do
|
||||
|
||||
1. **List all users** (requires admin authentication)
|
||||
2. **Enable password reset** for specific users only
|
||||
3. **Verify user identity** before enabling reset
|
||||
4. **Monitor password reset activity**
|
||||
|
||||
### 🔓 What Flagged Users CAN Do
|
||||
|
||||
1. **Reset password without authentication** (one-time only)
|
||||
2. **Only if admin has explicitly flagged them**
|
||||
3. **Within rate limits** (3 attempts/hour)
|
||||
|
||||
## 🛡️ Implementation Requirements
|
||||
|
||||
### Admin Endpoints (Require Authentication)
|
||||
|
||||
```http
|
||||
POST /api/v1/admin/users/{username}/allow-reset
|
||||
Headers:
|
||||
Authorization: Bearer <admin-jwt-token>
|
||||
X-Admin-Key: <master-admin-key>
|
||||
```
|
||||
|
||||
**Security Checks:**
|
||||
- ✅ Valid admin JWT token required
|
||||
- ✅ Admin privileges verified
|
||||
- ✅ User exists in database
|
||||
- ✅ Sets `allow_password_reset = true`
|
||||
|
||||
### User Reset Endpoint (No Auth Required)
|
||||
|
||||
```http
|
||||
POST /api/v1/auth/reset-password
|
||||
Body:
|
||||
{
|
||||
"username": "forgotten_user",
|
||||
"new_password": "secureNewPassword123!"
|
||||
}
|
||||
```
|
||||
|
||||
**Security Checks:**
|
||||
- ✅ User exists in database
|
||||
- ✅ `allow_password_reset = true` (admin must have set this)
|
||||
- ✅ Rate limit not exceeded (3 attempts/hour)
|
||||
- ✅ New password meets requirements
|
||||
- ✅ Automatically sets `allow_password_reset = false` after reset
|
||||
|
||||
## 📋 Security Test Cases
|
||||
|
||||
### BDD Test Scenarios
|
||||
|
||||
```gherkin
|
||||
Feature: Admin-Only Password Reset
|
||||
Scenario: Non-admin user cannot flag themselves for reset
|
||||
Given I am authenticated as a regular user
|
||||
When I try to POST to /api/v1/admin/users/myusername/allow-reset
|
||||
Then I should receive 403 Forbidden
|
||||
And the response should contain error "admin_required"
|
||||
|
||||
Scenario: Unauthenticated user cannot flag others for reset
|
||||
Given I am not authenticated
|
||||
When I try to POST to /api/v1/admin/users/otheruser/allow-reset
|
||||
Then I should receive 401 Unauthorized
|
||||
And the response should contain error "auth_unauthorized"
|
||||
|
||||
Scenario: User cannot reset password without admin flag
|
||||
Given I am not authenticated
|
||||
And user "forgotten_user" has allow_password_reset = false
|
||||
When I POST to /api/v1/auth/reset-password with username "forgotten_user"
|
||||
Then I should receive 403 Forbidden
|
||||
And the response should contain error "password_reset_not_allowed"
|
||||
|
||||
Scenario: Admin successfully enables password reset
|
||||
Given I am authenticated as admin
|
||||
And user "forgotten_user" exists
|
||||
When I POST to /api/v1/admin/users/forgotten_user/allow-reset
|
||||
Then I should receive 200 OK
|
||||
And user "forgotten_user" should have allow_password_reset = true
|
||||
|
||||
Scenario: Flagged user successfully resets password
|
||||
Given user "forgotten_user" has allow_password_reset = true
|
||||
When I POST to /api/v1/auth/reset-password with valid new password
|
||||
Then I should receive 200 OK
|
||||
And user password should be updated
|
||||
And user "forgotten_user" should have allow_password_reset = false
|
||||
```
|
||||
|
||||
## 🔧 Technical Implementation
|
||||
|
||||
### Database Model
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
// ... other fields
|
||||
AllowPasswordReset bool `gorm:"default:false"`
|
||||
// This field can ONLY be set to true by admin users
|
||||
}
|
||||
```
|
||||
|
||||
### Admin Service
|
||||
|
||||
```go
|
||||
type AdminService struct {
|
||||
userRepo user.UserRepository
|
||||
auth auth.AuthService
|
||||
}
|
||||
|
||||
// Only admins can call this method
|
||||
func (s *AdminService) AllowPasswordReset(ctx context.Context, username string) error {
|
||||
// Verify admin privileges from context
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return errors.New("admin required")
|
||||
}
|
||||
|
||||
// Set the flag - only admins can do this
|
||||
return s.userRepo.AllowPasswordReset(username)
|
||||
}
|
||||
```
|
||||
|
||||
### Password Reset Service
|
||||
|
||||
```go
|
||||
type AuthService struct {
|
||||
userRepo user.UserRepository
|
||||
}
|
||||
|
||||
// Anyone can call this, but it only works if admin flagged the user
|
||||
func (s *AuthService) ResetPasswordWithoutAuth(username, newPassword string) error {
|
||||
// Get user from database
|
||||
user, err := s.userRepo.GetUserByUsername(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// CRITICAL SECURITY CHECK
|
||||
if !user.AllowPasswordReset {
|
||||
return errors.New("password reset not allowed")
|
||||
}
|
||||
|
||||
// Update password
|
||||
if err := s.userRepo.UpdatePassword(username, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Clear the flag - one-time use only
|
||||
return s.userRepo.ClearPasswordResetFlag(username)
|
||||
}
|
||||
```
|
||||
|
||||
## 🛑 Security Threat Model
|
||||
|
||||
### Potential Threats & Mitigations
|
||||
|
||||
| Threat | Impact | Mitigation |
|
||||
|--------|--------|------------|
|
||||
| User flags themselves for reset | High | Admin authentication required for flagging |
|
||||
| User flags other users for reset | High | Admin authentication required for flagging |
|
||||
| Brute force password reset | Medium | Rate limiting (3 attempts/hour) |
|
||||
| Unauthorized admin access | Critical | Strong admin password + JWT security |
|
||||
| Replay attacks on reset | Medium | One-time flag clearing after reset |
|
||||
| Flag persistence after reset | Medium | Automatic flag clearing after successful reset |
|
||||
|
||||
## 📈 Security Metrics
|
||||
|
||||
1. **Admin-Only Flagging:** 100% of password reset flags set by admins
|
||||
2. **No Self-Service:** 0% of users can flag themselves
|
||||
3. **Rate Limit Compliance:** <3 reset attempts per hour per user
|
||||
4. **Flag Clearing:** 100% of flags cleared after successful reset
|
||||
|
||||
## 🎯 Compliance Requirements
|
||||
|
||||
### Security Standards
|
||||
- ✅ **OWASP Authentication Cheat Sheet** - Admin separation of duties
|
||||
- ✅ **CIS Controls** - Access control and account management
|
||||
- ✅ **GDPR** - No unnecessary personal data collection
|
||||
- ✅ **Zero Trust** - Explicit verification for sensitive operations
|
||||
|
||||
### Audit Requirements
|
||||
- ✅ All admin actions logged (who enabled reset for whom)
|
||||
- ✅ Password reset attempts logged
|
||||
- ✅ Failed attempts logged and rate limited
|
||||
- ✅ Admin authentication events logged
|
||||
|
||||
## 📚 References
|
||||
|
||||
- [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
|
||||
- [CIS Controls v8](https://www.cisecurity.org/controls/)
|
||||
- [GDPR Compliance Guide](https://gdpr-info.eu/)
|
||||
- [Zero Trust Architecture](https://www.nist.gov/zero-trust)
|
||||
|
||||
## 🎉 Summary
|
||||
|
||||
**Security Principle:** Only authenticated administrators can enable password reset for users
|
||||
|
||||
**User Experience:** Users must contact admin for password reset assistance
|
||||
|
||||
**Technical Implementation:** Admin-only endpoints with strict security checks
|
||||
|
||||
**Compliance:** Meets OWASP, CIS, GDPR, and Zero Trust standards
|
||||
|
||||
**Status:** Security policy documented and implemented ✅
|
||||
|
||||
---
|
||||
|
||||
*DanceLessonsCoach - Secure by design, private by default 🔒*
|
||||
648
documentation/technical/user-management-system.md
Normal file
648
documentation/technical/user-management-system.md
Normal file
@@ -0,0 +1,648 @@
|
||||
# User Management and Authentication System
|
||||
|
||||
## Overview
|
||||
|
||||
The DanceLessonsCoach user management and authentication system provides secure user authentication, personalized experiences, and administrative capabilities. This document describes the system architecture, API endpoints, and integration points.
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Client] -->|HTTP Request| B[Authentication Middleware]
|
||||
B -->|Valid Token| C[Authorized Endpoints]
|
||||
B -->|Invalid Token| D[401 Unauthorized]
|
||||
C --> E[Greet Service]
|
||||
C --> F[User Profile Service]
|
||||
C --> G[Admin Service]
|
||||
E -->|Personalized Response| A
|
||||
F -->|User Data| H[PostgreSQL]
|
||||
G -->|Admin Operations| H
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. User Model
|
||||
|
||||
**Database Schema:**
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
current_goal TEXT,
|
||||
is_admin BOOLEAN DEFAULT FALSE,
|
||||
allow_password_reset BOOLEAN DEFAULT FALSE,
|
||||
last_login TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
- `username`: Unique identifier (3-50 alphanumeric characters)
|
||||
- `password_hash`: bcrypt-hashed password
|
||||
- `description`: User's personal description
|
||||
- `current_goal`: User's current dance learning goal
|
||||
- `is_admin`: Administrative privileges flag
|
||||
- `allow_password_reset`: Flag for password reset eligibility
|
||||
|
||||
### 2. Authentication Service
|
||||
|
||||
**Features:**
|
||||
- JWT token generation and validation
|
||||
- bcrypt password hashing (work factor 12)
|
||||
- 30-minute token expiration
|
||||
- Secure cookie-based token storage
|
||||
- Admin master password authentication
|
||||
|
||||
**Environment Variables:**
|
||||
```bash
|
||||
# JWT Configuration
|
||||
DLC_JWT_SECRET="your-secure-random-secret-key"
|
||||
DLC_JWT_EXPIRATION="30m"
|
||||
|
||||
# Admin Configuration
|
||||
DLC_ADMIN_USERNAME="admin"
|
||||
DLC_ADMIN_MASTER_PASSWORD="secure-master-password"
|
||||
|
||||
# Database Configuration
|
||||
DLC_DB_HOST="localhost"
|
||||
DLC_DB_PORT="5432"
|
||||
DLC_DB_USER="dancecoach"
|
||||
DLC_DB_PASSWORD="secure-password"
|
||||
DLC_DB_NAME="dance_lessons_coach"
|
||||
DLC_DB_SSL_MODE="disable"
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication Endpoints
|
||||
|
||||
#### POST `/api/v1/auth/register`
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"username": "john_doe",
|
||||
"password": "securePassword123!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (201 Created):**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"created_at": "2024-04-06T10:00:00Z",
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
```
|
||||
|
||||
**Validation Rules:**
|
||||
- `username`: Required, 3-50 chars, alphanumeric only
|
||||
- `password`: Required, min 8 chars
|
||||
|
||||
#### POST `/api/v1/auth/login`
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"username": "john_doe",
|
||||
"password": "securePassword123!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"is_admin": false,
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"expires_at": "2024-04-06T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST `/api/v1/auth/reset-password`
|
||||
**Request (for flagged users only):**
|
||||
```json
|
||||
{
|
||||
"username": "john_doe",
|
||||
"new_password": "newSecurePassword456!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"message": "Password reset successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### User Profile Endpoints
|
||||
|
||||
#### GET `/api/v1/users/me`
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"description": "Dance enthusiast learning salsa",
|
||||
"current_goal": "Master basic salsa steps",
|
||||
"created_at": "2024-04-06T10:00:00Z",
|
||||
"last_login": "2024-04-06T10:15:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### PUT `/api/v1/users/me`
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"description": "Passionate dancer learning multiple styles",
|
||||
"current_goal": "Prepare for salsa competition"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"description": "Passionate dancer learning multiple styles",
|
||||
"current_goal": "Prepare for salsa competition",
|
||||
"updated_at": "2024-04-06T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### PUT `/api/v1/users/me/password`
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"current_password": "securePassword123!",
|
||||
"new_password": "evenMoreSecurePassword456!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"message": "Password updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Admin Endpoints
|
||||
|
||||
#### GET `/api/v1/admin/users`
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
X-Admin-Key: master-admin-key
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"is_admin": false,
|
||||
"allow_password_reset": false,
|
||||
"created_at": "2024-04-06T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"username": "jane_smith",
|
||||
"is_admin": true,
|
||||
"allow_password_reset": true,
|
||||
"created_at": "2024-04-05T15:30:00Z"
|
||||
}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
```
|
||||
|
||||
#### POST `/api/v1/admin/users/{username}/allow-reset`
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
X-Admin-Key: master-admin-key
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"message": "Password reset allowed for user john_doe"
|
||||
}
|
||||
```
|
||||
|
||||
#### DELETE `/api/v1/admin/users/{username}`
|
||||
**Headers:**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
X-Admin-Key: master-admin-key
|
||||
```
|
||||
|
||||
**Response (200 OK):**
|
||||
```json
|
||||
{
|
||||
"message": "User john_doe deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Greet Service
|
||||
|
||||
### Current Behavior
|
||||
```
|
||||
GET /api/v1/greet/John
|
||||
Response: {"message": "Hello John!"}
|
||||
```
|
||||
|
||||
### New Behavior with Authentication
|
||||
```
|
||||
GET /api/v1/greet
|
||||
Headers: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
Response: {"message": "Hello john_doe!"}
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
```go
|
||||
func (s *Service) Greet(ctx context.Context, name string) string {
|
||||
// Extract authenticated username from context
|
||||
username := auth.GetUsernameFromContext(ctx)
|
||||
|
||||
if username != "" {
|
||||
return "Hello " + username + "!"
|
||||
}
|
||||
|
||||
// Fallback to original behavior
|
||||
if name == "" {
|
||||
return "Hello world!"
|
||||
}
|
||||
return "Hello " + name + "!"
|
||||
}
|
||||
```
|
||||
|
||||
## Password Reset Workflow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Admin
|
||||
participant System
|
||||
participant Database
|
||||
|
||||
User->>System: Forgot password (no auth)
|
||||
System-->>User: 403 Forbidden
|
||||
|
||||
User->>Admin: Request password reset
|
||||
Admin->>System: POST /api/v1/admin/users/john_doe/allow-reset
|
||||
System->>Database: Set allow_password_reset = true
|
||||
Database-->>System: Success
|
||||
System-->>Admin: 200 OK
|
||||
|
||||
User->>System: POST /api/v1/auth/reset-password
|
||||
System->>Database: Check allow_password_reset flag
|
||||
Database-->>System: Flag is true
|
||||
System->>Database: Update password_hash
|
||||
Database-->>System: Success
|
||||
System->>Database: Set allow_password_reset = false
|
||||
System-->>User: 200 OK
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Password Storage
|
||||
- **Algorithm:** bcrypt with work factor 12
|
||||
- **Implementation:** `golang.org/x/crypto/bcrypt`
|
||||
- **Salt:** Automatic per-password salt
|
||||
|
||||
### JWT Security
|
||||
- **Algorithm:** HS256 with secure random key
|
||||
- **Expiration:** 30 minutes (configurable)
|
||||
- **Storage:** HTTP-only, Secure cookies
|
||||
- **Claims:** User ID, username, admin flag, expiration
|
||||
|
||||
### Rate Limiting
|
||||
- **Authentication Endpoints:** 5 requests per minute per IP
|
||||
- **Password Reset:** 3 attempts per hour per user
|
||||
- **Implementation:** Chi middleware
|
||||
|
||||
### Input Validation
|
||||
- **Username:** 3-50 alphanumeric characters
|
||||
- **Password:** Minimum 8 characters
|
||||
- **Description/Goal:** Maximum 500 characters
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Error Format
|
||||
```json
|
||||
{
|
||||
"error": "error_code",
|
||||
"message": "Human-readable message",
|
||||
"details": [
|
||||
{
|
||||
"field": "username",
|
||||
"message": "Username must be at least 3 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Common Error Codes
|
||||
- `auth_invalid_credentials`: Invalid username/password
|
||||
- `auth_token_expired`: JWT token expired
|
||||
- `auth_token_invalid`: Invalid JWT token
|
||||
- `auth_unauthorized`: Missing or invalid authorization
|
||||
- `validation_failed`: Input validation failed
|
||||
- `user_not_found`: User does not exist
|
||||
- `user_exists`: Username already taken
|
||||
- `password_reset_not_allowed`: User not flagged for reset
|
||||
- `admin_required`: Admin privileges required
|
||||
|
||||
## Database Setup
|
||||
|
||||
### Docker Compose
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: dance-lessons-coach-db
|
||||
environment:
|
||||
POSTGRES_USER: dancecoach
|
||||
POSTGRES_PASSWORD: secure-password
|
||||
POSTGRES_DB: dance_lessons_coach
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U dancecoach"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
```
|
||||
|
||||
### Database Migration
|
||||
```bash
|
||||
# Initialize database
|
||||
go run cmd/server/main.go migrate
|
||||
|
||||
# Run migrations
|
||||
goose -dir migrations/postgres up
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Password hashing/verification
|
||||
- JWT token generation/validation
|
||||
- User model validation
|
||||
- Repository methods
|
||||
|
||||
### Integration Tests
|
||||
- Authentication flow
|
||||
- Authorization middleware
|
||||
- Database operations
|
||||
- Password reset workflow
|
||||
|
||||
### BDD Tests
|
||||
```gherkin
|
||||
Feature: User Authentication
|
||||
Scenario: Successful user registration
|
||||
Given I am not authenticated
|
||||
When I register with valid credentials
|
||||
Then I should receive a JWT token
|
||||
And my user account should be created
|
||||
|
||||
Scenario: Successful login
|
||||
Given I have a registered account
|
||||
When I login with correct credentials
|
||||
Then I should receive a JWT token
|
||||
And the token should expire in 30 minutes
|
||||
|
||||
Scenario: Personalized greeting for authenticated user
|
||||
Given I am authenticated as "john_doe"
|
||||
When I request the default greeting
|
||||
Then the response should be "{"message":"Hello john_doe!"}"
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### New Dependencies
|
||||
```bash
|
||||
# Add to go.mod
|
||||
require (
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
gorm.io/gorm v1.25.0
|
||||
gorm.io/driver/postgres v1.5.0
|
||||
)
|
||||
```
|
||||
|
||||
### Pipeline Changes
|
||||
1. Add PostgreSQL service to CI environment
|
||||
2. Run database migrations before tests
|
||||
3. Include authentication tests in test suite
|
||||
4. Add security scanning for dependencies
|
||||
|
||||
## Deployment Considerations
|
||||
|
||||
### Configuration
|
||||
```yaml
|
||||
# config.yaml
|
||||
database:
|
||||
host: localhost
|
||||
port: 5432
|
||||
user: dancecoach
|
||||
password: secure-password
|
||||
name: dance_lessons_coach
|
||||
ssl_mode: disable
|
||||
|
||||
auth:
|
||||
jwt_secret: your-secure-random-secret-key
|
||||
jwt_expiration: 30m
|
||||
admin_username: admin
|
||||
admin_master_password: secure-master-password
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
```bash
|
||||
# Database
|
||||
export DLC_DB_HOST="localhost"
|
||||
export DLC_DB_PORT="5432"
|
||||
export DLC_DB_USER="dancecoach"
|
||||
export DLC_DB_PASSWORD="secure-password"
|
||||
export DLC_DB_NAME="dance_lessons_coach"
|
||||
export DLC_DB_SSL_MODE="disable"
|
||||
|
||||
# Authentication
|
||||
export DLC_JWT_SECRET="your-secure-random-secret-key"
|
||||
export DLC_JWT_EXPIRATION="30m"
|
||||
export DLC_ADMIN_USERNAME="admin"
|
||||
export DLC_ADMIN_MASTER_PASSWORD="secure-master-password"
|
||||
```
|
||||
|
||||
## Monitoring and Observability
|
||||
|
||||
### Metrics
|
||||
- `auth_login_success_total`: Successful logins
|
||||
- `auth_login_failure_total`: Failed login attempts
|
||||
- `auth_register_total`: User registrations
|
||||
- `auth_token_issued_total`: JWT tokens issued
|
||||
- `user_active_total`: Active users
|
||||
|
||||
### Logging
|
||||
- Authentication attempts (success/failure)
|
||||
- Password changes
|
||||
- Admin operations
|
||||
- Rate limiting events
|
||||
|
||||
### Alerts
|
||||
- Multiple failed login attempts
|
||||
- Admin account modifications
|
||||
- Unusual password reset activity
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Short-term (Next 3 Months)
|
||||
1. **Refresh Tokens:** Long-lived refresh tokens with rotation
|
||||
2. **Rate Limiting:** IP-based rate limiting for auth endpoints
|
||||
3. **Password Strength:** Enforce stronger password requirements
|
||||
4. **Account Lockout:** Temporary lockout after failed attempts
|
||||
|
||||
### Medium-term (Next 6 Months)
|
||||
1. **Multi-factor Authentication:** TOTP or backup codes
|
||||
2. **User Activity Logging:** Comprehensive audit trails
|
||||
3. **Session Management:** View and revoke active sessions
|
||||
4. **Password Expiration:** Enforce periodic password changes
|
||||
|
||||
### Long-term (Future)
|
||||
1. **OAuth Integration:** Google, Facebook, Apple sign-in
|
||||
2. **Social Features:** User profiles, followers, messaging
|
||||
3. **Role-Based Access:** Fine-grained permissions
|
||||
4. **User Preferences:** Theme, language, notifications
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Issue:** Authentication fails with valid credentials
|
||||
- **Check:** Password hash comparison logic
|
||||
- **Check:** JWT secret key configuration
|
||||
- **Check:** Database connection
|
||||
|
||||
**Issue:** Password reset not working
|
||||
- **Check:** User has `allow_password_reset` flag set
|
||||
- **Check:** Admin has set the flag correctly
|
||||
- **Check:** Rate limiting not blocking requests
|
||||
|
||||
**Issue:** Personalized greeting not showing username
|
||||
- **Check:** Authentication middleware is properly configured
|
||||
- **Check:** JWT token is valid and not expired
|
||||
- **Check:** Context contains username after authentication
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From No Authentication to User System
|
||||
|
||||
1. **Add Dependencies:**
|
||||
```bash
|
||||
go get github.com/golang-jwt/jwt/v5
|
||||
go get golang.org/x/crypto
|
||||
go get gorm.io/gorm
|
||||
go get gorm.io/driver/postgres
|
||||
```
|
||||
|
||||
2. **Update Configuration:**
|
||||
- Add database configuration
|
||||
- Add JWT configuration
|
||||
- Add admin configuration
|
||||
|
||||
3. **Update Server:**
|
||||
- Add authentication middleware
|
||||
- Add user repository initialization
|
||||
- Add auth routes
|
||||
|
||||
4. **Update Greet Service:**
|
||||
- Modify to check for authenticated username
|
||||
- Maintain backward compatibility
|
||||
|
||||
5. **Update Tests:**
|
||||
- Add authentication scenarios
|
||||
- Update existing tests for new behavior
|
||||
- Add BDD tests for user management
|
||||
|
||||
6. **Update CI/CD:**
|
||||
- Add PostgreSQL to test environment
|
||||
- Update test scripts
|
||||
- Add security scanning
|
||||
|
||||
## References
|
||||
|
||||
- [GORM Documentation](https://gorm.io/)
|
||||
- [JWT RFC 7519](https://tools.ietf.org/html/rfc7519)
|
||||
- [bcrypt Documentation](https://pkg.go.dev/golang.org/x/crypto/bcrypt)
|
||||
- [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
|
||||
- [Chi Router Middleware](https://github.com/go-chi/chi)
|
||||
|
||||
## Appendix
|
||||
|
||||
### Username Validation Regex
|
||||
```go
|
||||
var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9]{3,50}$`)
|
||||
```
|
||||
|
||||
### Password Hashing Example
|
||||
```go
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
func CheckPasswordHash(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
```
|
||||
|
||||
### JWT Token Example
|
||||
```go
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Claims struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func GenerateJWT(user *User, secret string, expiration time.Duration) (string, error) {
|
||||
claims := &Claims{
|
||||
UserID: user.ID,
|
||||
Username: user.Username,
|
||||
IsAdmin: user.IsAdmin,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiration)),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString([]byte(secret))
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user