Commit Graph

50 Commits

Author SHA1 Message Date
Benjamin cd0b751966 fix: ensures the SessionService is created whenever ANY authentication method is enabled. 2025-12-18 11:44:10 +01:00
Benjamin 19cda55de9 fix: missing ci ackify_app role creation 2025-12-16 00:25:20 +01:00
Benjamin 44431dabf4 feat(rls): move ackify_app role creation from init script to migrate tool
BREAKING CHANGE: ACKIFY_APP_PASSWORD environment variable is now required for RLS support. The migrate tool creates the ackify_app role before running migrations, ensuring compatibility with existing deployments.

Changes:
- Add ensureAppRole() in cmd/migrate to create/update ackify_app role
- Remove docker/init-scripts/01-create-app-user.sh (no longer needed)
- Update compose.yml: add ACKIFY_APP_PASSWORD, backend connects as ackify_app
- Update migration 0016: remove conditional role creation
- Add RLS documentation (docs/en/configuration/rls.md, docs/fr/configuration/rls.md)
- Update configuration docs with RLS section and security checklist

Migration path for existing deployments:
1. Set ACKIFY_APP_PASSWORD in .env
2. Run docker compose up (migrate will create the role automatically)
2025-12-15 23:59:06 +01:00
Benjamin eca55c6bcb refactor(go): restore go.mod (and sum) on project root
and restore missing cypress tests
2025-12-10 16:29:00 +01:00
Benjamin ab6cdbb383 refactor(go): rename module to github.com/btouchard/ackify-ce/backend
Enable importing backend packages in SaaS project by aligning module
path with its location in the repository.
2025-12-08 19:01:28 +01:00
Benjamin 9b7a289a2e fix: remove invalid coreapp from merge 2025-12-08 16:31:37 +01:00
Benjamin 02b2ed0bb5 fix(merge): resolve conflits 2025-12-08 16:13:10 +01:00
Benjamin 24e2de2922 refactor(arch): enforce strict layered architecture with private interfaces
Apply Clean Architecture principles throughout the codebase to eliminate tight coupling between layers. Handlers now depend exclusively on services through private interfaces, never directly on repositories.
Introduce a ServerBuilder pattern with pluggable capability providers.

refactor(auth): introduce injectable AuthorizerService

Replace hardcoded AdminEmails and OnlyAdminCanCreate config fields
with an injectable AuthorizerService. This improves testability and
follows the dependency injection pattern used elsewhere in the codebase.

- Create AuthorizerService in application/services/
- Define minimal Authorizer interfaces in consuming packages
- Update middleware, handlers, and router to use injected authorizer
- Update all affected tests with mock implementations

refactor(build): move go.mod to backend directory
Move Go module files from project root to backend/ directory while keeping the module name as github.com/btouchard/ackify-ce.
This improves project structure by keeping Go-specific files within the Go codebase directory.

# Conflicts:
#	backend/internal/application/services/checksum_service_test.go
#	backend/internal/application/services/document_service.go
#	backend/internal/application/services/document_service_duplicate_test.go
#	backend/internal/application/services/document_service_test.go
#	backend/internal/presentation/api/documents/handler.go
#	backend/internal/presentation/api/documents/handler_test.go
#	backend/internal/presentation/api/router.go
#	backend/pkg/web/server.go
2025-12-08 16:07:03 +01:00
Benjamin 1b108ed874 refacto(backend): extract coreapp packages for DI and authorization
- Add pkg/coreapp/ with service interfaces and dependency injection
- Add DocumentAuthorizer for document access control
- Add ExpectedSignerService for expected signers management
- Simplify router and handlers by using coreapp dependencies
2025-12-04 15:19:01 +01:00
Benjamin 796d327442 feat(tenant): add tenant support
- Add instance_metadata table with unique UUID per instance
- Add tenant_id column to all business tables (documents, signatures, expected_signers, webhooks, reminder_logs, email_queue, checksum_verifications, webhook_deliveries)
- Backfill existing data with instance tenant UUID
- Create TenantProvider interface and SingleTenantProvider implementation
- Update all repositories to filter by tenant_id
- Add immutability triggers to prevent tenant_id modification after creation

Migration 0015 includes:
- Schema changes with indexes for tenant_id columns
- SQL backfill for existing data
- Trigger functions for data integrity
2025-12-03 23:46:09 +01:00
Benjamin 249849b3ed feat(tenant): add tenant support
- Add instance_metadata table with unique UUID per instance
- Add tenant_id column to all business tables (documents, signatures, expected_signers, webhooks, reminder_logs, email_queue, checksum_verifications, webhook_deliveries)
- Backfill existing data with instance tenant UUID
- Create TenantProvider interface and SingleTenantProvider implementation
- Update all repositories to filter by tenant_id
- Add immutability triggers to prevent tenant_id modification after creation

Migration 0015 includes:
- Schema changes with indexes for tenant_id columns
- SQL backfill for existing data
- Trigger functions for data integrity
2025-12-03 22:24:12 +01:00
Benjamin 686edc6123 feat(mail): fix mail suject alway in english, now is based on i18n 2025-12-03 12:21:44 +01:00
Benjamin aa002f824c feat(db): improve db migrations system with force & goto command (for existing db without migration schema) 2025-12-03 11:01:23 +01:00
Benjamin 5261dce49e fix(oauth): add Microsoft Graph API field mapping support
Support Microsoft-specific user info fields:
  - email: check 'mail' and 'userPrincipalName' as fallbacks
  - name: check 'displayName' (camelCase) for Microsoft Graph API
2025-12-01 12:19:42 +01:00
Benjamin 71a479d953 feat(csv): import expected signature from CSV 2025-11-27 09:03:38 +01:00
Benjamin 533e62fcfe feat(csv): import expected signature from CSV 2025-11-26 23:37:21 +01:00
Benjamin a7891618c1 feat: comprehensive CI/CD refactoring with unified code coverage
Reorganize GitHub Actions workflows into reusable components and implement
complete code coverage tracking across backend, frontend, and E2E tests.

**CI/CD Improvements:**
- Split monolithic ci.yml into 6 specialized reusable workflows
- New workflows: test-backend, test-frontend, test-e2e, build-docker, security, coverage-report
- Orchestrated execution with proper dependencies and parallel jobs
- Codecov integration with multi-flag coverage (backend/frontend/e2e)

**Frontend Testing:**
- Add Vitest for unit testing with coverage-v8 provider
- Create test setup with window mocks for Ackify globals
- Add 34 unit tests for titleExtractor, referenceDetector, and http utils
- Configure Istanbul instrumentation for E2E coverage collection
- Integrate @cypress/code-coverage for E2E test coverage

**Test Infrastructure:**
- Create run-tests-suite.sh for local comprehensive test execution
- Proper Docker Compose orchestration for integration and E2E tests
- Automatic cleanup handlers with trap for test environments
- Coverage summary aggregation across all test types

**Bug Fixes:**
- Fix backend config tests after OAuth/MagicLink validation changes
- Update tests from panic expectations to error checking
- Ensure OAUTH_COOKIE_SECRET is properly configured in tests

**Configuration:**
- Add .codecov.yml for coverage reporting with flags
- Add .nycrc.json for E2E LCOV generation
- Update .gitignore for test artifacts and coverage reports
- Configure Vite for test environment and code instrumentation
2025-11-23 23:36:02 +01:00
Benjamin 5cd91654e0 feat: configurable rate limiting and comprehensive E2E test suite
Rate Limiting Configuration:
- Add ACKIFY_AUTH_MAGICLINK_RATE_LIMIT_EMAIL (default: 3/hour)
- Add ACKIFY_AUTH_MAGICLINK_RATE_LIMIT_IP (default: 10/hour)
- Add ACKIFY_AUTH_RATE_LIMIT (default: 5/min)
- Add ACKIFY_DOCUMENT_RATE_LIMIT (default: 10/min)
- Add ACKIFY_GENERAL_RATE_LIMIT (default: 100/min)

E2E Test Suite:
- 01-signature-workflow: Complete signature flow validation
- 02-signature-uniqueness: Constraint enforcement and duplicate prevention
- 03-admin-signers-management: Expected signers CRUD operations
- 04-admin-email-reminders: SMTP reminder functionality
- 05-document-creation-by-url: URL-based document initialization
- 06-my-signatures-page: User signature list and navigation
- 07-admin-document-deletion: Cascade deletion verification
- 08-admin-route-protection: Access control validation
- 09-complete-workflow: End-to-end multi-user scenario
- 10-unexpected-signatures: Handling of non-expected signers
2025-11-23 22:27:55 +01:00
Benjamin 779aada760 fix(test): fix configuration for e2e tests and improve en var loading for gracefull stop on error 2025-11-23 11:44:41 +01:00
Benjamin e885c63f92 test: remove trivial and redundant tests for better maintainability 2025-11-23 11:23:41 +01:00
Benjamin eecb2565bc refactor(checksum): propagate context for HTTP request cancellation
Add context.Context parameter to checksum computation functions
to enable request cancellation, timeout propagation, and better
observability for remote document downloads.
2025-11-23 01:01:40 +01:00
Benjamin ddb44df7d0 refactor(crypto): propagate context.Context for observability support
Add context.Context parameter to cryptographic signature operations
to enable distributed tracing, timeout propagation, and cancellation
handling throughout the signature creation pipeline.

This is a breaking change for the cryptoSigner interface but
maintains backward compatibility at the API level.
2025-11-23 00:50:18 +01:00
Benjamin 353b720453 feat(email): add jitter to retry logic to prevent thundering herd
Improve the email retry mechanism by adding 0-30% random jitter
to the exponential backoff calculation. This prevents multiple
failed emails from retrying at exactly the same time, which could
cause load spikes on the SMTP server.

Example retry times (with jitter):
- 1st retry: 1.0-1.3 minutes
- 2nd retry: 2.0-2.6 minutes
- 3rd retry: 4.0-5.2 minutes
2025-11-23 00:43:54 +01:00
Benjamin 3811741401 feat(database): configure PostgreSQL connection pool settings
Add connection pool configuration to optimize database performance and resource usage
2025-11-23 00:37:34 +01:00
Benjamin f6c75b3497 refactor(security): use structured logger to prevent sensitive data exposure
Changes:
- Replace fmt.Println with logger.Logger.Warn in config loading
- Remove client_id and auth URLs from OAuth provider logs
- Remove user email, name, and sub from authentication logs
- Use structured logging for worker shutdown errors
- Fix MagicLink status logging (only log when actually enabled)
2025-11-23 00:36:49 +01:00
Benjamin 066374ca33 refactor(api): centralize pagination logic in shared package
Changes:
- Add PaginationParams struct for query parameter handling
- Add ParsePaginationParams() to parse and validate pagination from HTTP requests
- Add Validate() method with configurable min/max constraints
- Support both 'limit' and 'page_size' query parameters for flexibility
- Migrate documents handler (default: 20/page, max: 100)
- Migrate admin handler (default: 100/page, max: 200)
- Remove duplicated strconv imports and validation logic
2025-11-23 00:22:29 +01:00
Benjamin 0c3ba254ee feat(email): implement smart error categorization and adaptive retry strategy
Changes:
- Add EmailErrorType enum (Retryable, Permanent, RateLimited)
- Implement categorizeError() to analyze SMTP codes and error messages
  - Detect permanent errors (5xx codes, invalid emails)
  - Detect rate limiting (421/429/450 codes)
  - Detect temporary errors (4xx codes, network/DNS issues)
- Implement calculateRetryDelay() with adaptive backoff strategies
  - Retryable: 1min × 2^retry (standard exponential backoff)
  - RateLimited: 5min × 3^retry (aggressive backoff)
  - Permanent: no retry
- Add MarkAsFailedWithDelay() to repository for custom retry delays
- Maintain backward compatibility with existing MarkAsFailed()
2025-11-23 00:22:15 +01:00
Benjamin 93d9e2e575 feat(search): implement full-text document search across public and admin APIs
Backend:
- Add Search() method to DocumentRepository for ILIKE pattern matching on doc_id, title, url, description
- Add Count() method to accurately count total matching documents (used for pagination)
- Update admin handler to support search query parameter with proper pagination
- Implement full public documents API with search support (previously stub)
- Update test mocks to include new repository methods

Frontend:
- Replace client-side filtering with server-side search in admin dashboard
- Add debounced search input (300ms delay) to reduce API calls
- Separate loading states: initial page load vs. search/pagination (prevents input focus loss)
- Add visual feedback: spinning loader icon in search field during active search
- Enable pagination during search (previously disabled)
- Pass search parameter to API service
2025-11-23 00:21:59 +01:00
ArnaudFra ec947afc24 feat(mail): add ACKIFY_MAIL_INSECURE_SKIP_VERIFY option (#6)
* feat(mail): add option to skip TLS certificate verification

Add ACKIFY_MAIL_INSECURE_SKIP_VERIFY environment variable to allow
bypassing TLS certificate verification for self-signed certificates.

This is useful for development/testing environments with self-signed
SMTP certificates while maintaining secure defaults (false by default).

* docs: add ACKIFY_MAIL_INSECURE_SKIP_VERIFY documentation
2025-11-22 22:20:34 +01:00
Benjamin da1f300d2d feat(admin): improve email reminders UX and fix signer deletion
Backend changes:
- Add SMTPEnabled flag to distinguish SMTP service from MagicLink authentication
- Fix URL-encoded email decoding in DELETE /signers/{email} endpoint
- Add detailed error logging for expected signer removal operations
- Initialize ReminderAsync and MagicLink services unconditionally
- Update config tests to reflect new MagicLink requires explicit enabling

Frontend changes:
- Add ACKIFY_SMTP_ENABLED window variable for feature detection
- Hide delete button for expected signers who have already signed
- Show email reminders card only when SMTP enabled or history exists
- Display informative alert when SMTP disabled but reminder history present
- Add i18n translations for email service disabled message (5 languages)

These changes improve admin experience by preventing invalid operations
(deleting signers who signed, sending emails without SMTP) and providing
clear feedback about feature availability.
2025-11-22 00:43:35 +01:00
Benjamin 34146fb02d fix(auth): enable logout for MagicLink users
- Move /logout route outside OAuth-only block to support both OAuth
and MagicLink authentication methods.

- Also fix notification icon alignment in AuthChoicePage.

- And add press-kit inside /docs
2025-11-08 00:02:35 +01:00
Benjamin aa5fee90f6 feat(admin): add option to restrict document creation to admins only
Add new configuration option ACKIFY_ONLY_ADMIN_CAN_CREATE (default: false) to control who can create documents.

Backend changes:
- Add OnlyAdminCanCreate config field to AppConfig
- Implement authorization checks in document handlers
- Protect POST /documents and GET /documents/find-or-create endpoints
- Add unit tests for admin-only document creation (4 tests)

Frontend changes:
- Inject ACKIFY_ONLY_ADMIN_CAN_CREATE to window object
- Hide DocumentForm component for non-admin users when enabled
- Add i18n translations (en, fr, es, de, it)
- Display warning message for non-admin users

Documentation:
- Update .env.example files with new variable
- Update configuration docs (en/fr)
- Update install script to prompt for restriction option
- Update install/README.md

When enabled, only users listed in ACKIFY_ADMIN_EMAILS can create new documents. Both direct creation and find-or-create endpoints are protected.
2025-11-06 16:08:03 +01:00
Benjamin a27f051838 feat(auth): implement reminder authentication tokens
Add authentication tokens embedded in reminder emails allowing users to
authenticate and sign documents in one click.

Changes:
- Add 'purpose' (login/reminder_auth) and 'doc_id' columns to magic_link_tokens
- Implement CreateReminderAuthToken (24h validity) and VerifyReminderAuthToken
- Create reminder auth handler and route (/api/v1/auth/reminder-link/verify)
- Update ReminderService and ReminderAsyncService to generate auth tokens
- Fix table name mismatch: magic_links → magic_link_tokens throughout
- Reorder service initialization in server.go for proper dependencies

Token validity:
- Magic Link: 15 minutes (login)
- Reminder Auth: 24 hours (document signature)

The reminder auth flow:
1. Admin sends reminder
2. User receives email with auth link
3. User clicks link → auto-authenticated if not logged in
4. User redirected to document signature page
5. If already authenticated with correct account, skip auth step
2025-11-06 13:43:49 +01:00
Benjamin 46e6bdab24 feat(auth): magic link email now uses frontend language
The magic link email is now sent in the same language as the frontend
at the time of the request, matching the behavior of reminder emails.

Changes:
- Modified MagicLinkService.RequestMagicLink to accept locale parameter
- Handler extracts locale using i18n.GetLangFromRequest() from HTTP headers
- Falls back to "en" if locale is empty
- Consistent with reminder email locale detection
2025-11-06 12:04:23 +01:00
Benjamin cff602c812 fix(tests): update OAuth tests to use correct signature URL path
Change test URLs from '/sign?doc=' to '/?doc=' to match the correct
frontend route after the URL path correction in reminder emails.

Updated base64 encoded state in callback test:
- Old: L3NpZ24_ZG9jPXRlc3Q (decodes to /sign?doc=test)
- New: Lz9kb2M9dGVzdA (decodes to /?doc=test)
2025-11-06 01:15:45 +01:00
Benjamin 7b1f69ade6 fix(email): correct signature reminder URL path
Change signature URL in reminder emails from '/sign?doc=' to '/?doc='
to match the correct frontend route.

Updated in:
- ReminderService.sendSingleReminder
- ReminderAsyncService.queueSingleReminder
- Email helper tests
2025-11-06 01:09:44 +01:00
Benjamin 2dd7d8686c fix(email): correct SMTP TLS/STARTTLS configuration for Gmail
Backend changes:
- Use 'else if' to prevent activating both TLS and STARTTLS simultaneously
- Add StartTLSPolicy = MandatoryStartTLS for proper STARTTLS enforcement
- Add comments explaining TLS modes (implicit SSL vs explicit STARTTLS)

Install script changes:
- Auto-detect TLS configuration based on port number
- Port 465 → TLS=true, STARTTLS=false (implicit SSL)
- Port 587 → TLS=false, STARTTLS=true (explicit TLS/STARTTLS)
- Non-standard ports → manual configuration with clear prompts

This fixes timeout errors when sending emails via Gmail SMTP (port 587)
which requires STARTTLS, not direct TLS connection.
2025-11-06 00:05:16 +01:00
Benjamin c88508897f fix: github actions tests runs 2025-11-05 20:43:13 +01:00
Benjamin 32b469f04e feat: add magic link authentication
- Now can activate OIDC and/or MagicLink for user authentication.
- Add page to choose authentication method (if only OIDC is enabled, auto redirecting to login screen)
2025-11-05 15:01:23 +01:00
Benjamin f4b3430f06 feat(document): improve better title extraction 2025-10-28 00:06:18 +01:00
Benjamin 289f8cd53b fix(embed): add middleware to authorize embed create document (with hard rate limit) 2025-10-27 23:24:07 +01:00
Benjamin 44c8cbef04 feat(vers): add version number to front 2025-10-27 18:34:27 +01:00
Benjamin 925d363ac3 feat(webhook): ajout de la prise en charge des webhooks signés
- Envoi des événements vers des URLs configurées
- Signature HMAC-SHA256 via en-tête X-Signature (secret partagé)
- Retentatives avec backoff exponentiel et jitter
- Timeout réseau et gestion des erreurs/transitoires
- Idempotence par event_id et journalisation structurée
- Paramètres: WEBHOOK_URLS, WEBHOOK_SECRET, WEBHOOK_TIMEOUT_MS, WEBHOOK_MAX_RETRIES
2025-10-27 15:11:06 +01:00
Benjamin 714f122cfa fix: missing database integration test 2025-10-26 21:52:32 +01:00
Benjamin ee02df0287 fix: use random hex for test database names to prevent collisions
- Replace nanosecond+pid with crypto/rand generated hex (16 chars)
- Fixes race condition where parallel tests starting at same nanosecond
  would share the same database name
- Ensures true isolation even with hundreds of concurrent tests
- Resolves duplicate key constraint violations in CI (perf-doc, test-doc, etc.)
2025-10-26 16:21:43 +01:00
Benjamin 8ca23ce736 fix: ensure migrations directory is found in CI tests
- Fix migration path lookup to check both './migrations' and './backend/migrations'
- Remove hardcoded test schema in admin handler tests
- Use database.SetupTestDB which applies all migrations automatically
- Ensures test schema matches production schema with all columns (deleted_at, doc_checksum, etc.)
- Fixes test failures in CI where admin handler tests returned empty responses
2025-10-26 13:57:06 +01:00
Benjamin 2410653f63 fix: integrations tests concurency + missing column due a hardcoded database struct (removed) 2025-10-26 13:37:40 +01:00
Benjamin c738763ac3 fix: integrations tests concurency 2025-10-26 11:37:41 +01:00
Benjamin 68426bc882 feat: add PKCE support to OAuth2 flow for enhanced security
- Implement PKCE (Proof Key for Code Exchange) with S256 method
- Add crypto/pkce module with code verifier and challenge generation
- Modify OAuth flow to include code_challenge in authorization requests
- Update HandleCallback to validate code_verifier during token exchange
- Extend session lifetime from 7 to 30 days
- Add comprehensive unit tests for PKCE functions
- Maintain backward compatibility with fallback for non-PKCE sessions
- Add detailed logging for OAuth flow with PKCE tracking

PKCE enhances security by preventing authorization code interception
attacks, as recommended by OAuth 2.1 and OIDC standards.

feat: add encrypted refresh token storage with automatic cleanup

- Add oauth_sessions table for storing encrypted refresh tokens
- Implement AES-256-GCM encryption for refresh tokens using cookie secret
- Create OAuth session repository with full CRUD operations
- Add SessionWorker for automatic cleanup of expired sessions
- Configure cleanup to run every 24h for sessions older than 37 days
- Modify OAuth flow to store refresh tokens after successful authentication
- Track client IP and user agent for session security validation
- Link OAuth sessions to user sessions via session ID
- Add comprehensive encryption tests with security validations
- Integrate SessionWorker into server lifecycle with graceful shutdown

This enables persistent OAuth sessions with secure token storage,
reducing the need for frequent re-authentication from 7 to 30 days.
2025-10-26 02:32:10 +02:00
Benjamin e95185f9c7 feat: migrate to Vue.js SPA with API-first architecture
Major refactoring to modernize the application architecture:

Backend changes:
- Restructure API with v1 versioning and modular handlers
- Add comprehensive OpenAPI specification
- Implement RESTful endpoints for documents, signatures, admin
- Add checksum verification system for document integrity
- Add server-side runtime injection of ACKIFY_BASE_URL and meta tags
- Generate dynamic Open Graph/Twitter Card meta tags for unfurling
- Remove legacy HTML template handlers
- Isolate backend source on dedicated folder
- Improve tests suite

Frontend changes:
- Migrate from Go templates to Vue.js 3 SPA with TypeScript
- Add Tailwind CSS with shadcn/vue components
- Implement i18n support (fr, en, es, de, it)
- Add admin dashboard for document and signer management
- Add signature tracking with file checksum verification
- Add embed page with sign button linking to main app
- Implement dark mode and accessibility features
- Auto load file to compute checksum

Infrastructure:
- Update Dockerfile for SPA build process
- Simplify deployment with embedded frontend assets
- Add migration for checksum_verifications table

This enables better UX, proper link previews on social platforms,
and provides a foundation for future enhancements.
2025-10-26 02:32:10 +02:00