41 KiB
Status
Approved
Story
As a system administrator, I want to create and manage training institutions with multiple admin users (super and regular admins), so that private training institutions can manage their cohorts independently.
Acceptance Criteria
- Database schema for institutions and admin roles exists
- Super admins can create institutions and invite other admins
- Regular admins can manage cohorts within their institution
- Admins cannot access other institutions' data
- Role-based permissions are enforced at API and UI levels
Tasks / Subtasks
Database Setup (AC: #1) - WINSTON'S ARCHITECTURAL REVISION
Critical: 4-Layer Data Isolation Foundation
-
Migration 1: Add institution_id to account_access
- Add
institution_idreference toaccount_accessestable (nullable initially) - Add foreign key constraint:
fk_account_accesses_to_institutions - Backfill existing data: Link users to their institution via account
- Make non-nullable:
change_column_null :account_access, :institution_id, false - Add unique index:
[user_id, institution_id](prevents duplicate roles)
- Add
-
Migration 2: Update institutions table
- Add fields: account_id, name, registration_number, address, contact_email, contact_phone, super_admin_id, settings
- Add indexes: account_id (unique), registration_number (unique within account), super_admin_id
- Add foreign key: super_admin_id → users.id
-
Migration 3: Create cohort_admin_invitations table
- Add fields: email, hashed_token, token_preview, role, institution_id, sent_at, expires_at, used_at, created_by_id
- Add indexes: institution_id, email, expires_at
- Important: Store SHA-256 hashed tokens, never plaintext
-
Migration 4: Update account_access roles
- Add cohort_admin and cohort_super_admin to role enum
- Add index on role for performance
-
Rollback Strategy (CRITICAL)
- Test rollback on production-like data
- Document step-by-step rollback procedure
- Verify no data loss during rollback
Models (AC: #1, #2, #3, #4) - WINSTON'S 4-LAYER ISOLATION
Layer 1: Institution Model (Foundation)
- Create
app/models/institution.rb- Add belongs_to :account
- Add belongs_to :super_admin (class_name: 'User')
- Add has_many :cohorts
- Add has_many :sponsors
- Add has_many :account_accesses (NEW - critical for isolation)
- Add validations (name, registration_number, uniqueness within account)
- CRITICAL SCOPE:
scope :for_user, ->(user) { where(id: user.institutions.select(:id)) } - CRITICAL SCOPE:
scope :managed_by, ->(user) { where(super_admin_id: user.id) } - CRITICAL METHOD:
def accessible_by?(user) account_accesses.exists?(user_id: user.id) end
Layer 2: User Model Extension
- Extend
app/models/user.rb(add to existing)- Add has_many :account_accesses, dependent: :destroy
- Add has_many :institutions, through: :account_accesses
- Add has_many :managed_institutions, class_name: 'Institution', foreign_key: 'super_admin_id'
- CRITICAL METHOD:
def can_access_institution?(institution) institutions.exists?(institution.id) || managed_institutions.exists?(institution.id) end - CRITICAL METHOD:
def cohort_super_admin? account_accesses.exists?(role: 'cohort_super_admin') end - CRITICAL METHOD:
def cohort_admin? account_accesses.exists?(role: 'cohort_admin') end
Layer 3: Account Access Model (Security Core)
- Extend
app/models/account_access.rb- Add belongs_to :institution (NEW - critical for isolation)
- Add cohort_admin and cohort_super_admin to role enum
- CRITICAL VALIDATION:
validates :user_id, uniqueness: { scope: :institution_id } - Add scopes:
cohort_admins,cohort_super_admins,for_institution(institution)
Layer 4: Invitation Token Model (Cryptographic Security)
- Create
app/models/cohort_admin_invitation.rb-
Add belongs_to :institution
-
Add belongs_to :created_by, class_name: 'User'
-
CRITICAL: Store
hashed_token(SHA-256), never plaintext -
Add
token_previewfor debugging (first 8 chars + '...') -
CRITICAL METHODS:
def generate_token- uses SecureRandom.urlsafe_base64(64)def valid_token?(raw_token)- validates hash + Redis single-usedef expired?- checks expires_atdef used?- checks used_at
-
Validations: email format, role inclusion, expires_at presence
-
Scopes:
active,used,cleanup_expired -
Rate limiting: Max 5 pending invitations per email
-
Layer 5: Security Event Model (Audit Trail)
- Create
app/models/security_event.rb- Add user_id, event_type, ip_address, details (jsonb)
- Add indexes: user_id, event_type, created_at
- CRITICAL METHOD:
def self.log(event_type, user, details = {})
API Controllers (AC: #2, #3, #4, #5) - WINSTON'S 4-LAYER SECURITY
Layer 1: API Base Controller Security Extensions
- Extend
app/controllers/api/api_base_controller.rb- CRITICAL: Add
verify_institution_accessbefore_actiondef verify_institution_access return true unless params[:institution_id].present? institution = Institution.find_by(id: params[:institution_id]) unless institution && current_user.can_access_institution?(institution) log_security_event(:unauthorized_institution_access) render json: { error: 'Access denied to this institution' }, status: :forbidden return false end true end - CRITICAL: Add
verify_institution_role(required_role)method - CRITICAL: Add
log_security_event(event_type, details)method - Add security event logging for all authorization failures
- CRITICAL: Add
Layer 2: Institutions Controller
- Create
app/controllers/api/v1/institutions_controller.rb- Add
before_action :verify_institution_access(except index/create) - Authorization:
create- requires cohort_super_admin role on accountupdate/destroy- requires cohort_super_admin role + ownershipindex/show- requires any institution access
- Implement actions with 4-layer isolation:
def index @institutions = Institution.for_user(current_user) # Layer 1 scope end def show @institution = Institution.for_user(current_user).find(params[:id]) # Layer 1 + 2 end - Add strong parameters with validation
- Add error handling with security event logging
- Add
Layer 3: Admin Invitations Controller
- Create
app/controllers/api/v1/admin/invitations_controller.rb- CRITICAL: Rate limiting - max 5 pending invitations per email
- CRITICAL: Redis-backed single-use token enforcement
- Actions:
create- cohort_super_admin only, email validation, rate limitindex- scoped to institution, cohort_admin+ accessrevoke- cohort_super_admin only, mark used_at
- CRITICAL: Use
InvitationServicefor business logic - Email delivery via
CohortAdminInvitationJob(async)
Layer 4: Invitation Acceptance Controller
- Create
app/controllers/api/v1/admin/invitation_acceptance_controller.rb- CRITICAL: Token validation with Redis single-use check
- Email verification (token only valid for matching email)
- Create
AccountAccessrecord on successful acceptance - One-time use only - immediate invalidation
Layer 5: Security Event Controller (Monitoring)
- Create
app/controllers/api/v1/admin/security_events_controller.rb- cohort_super_admin only access
- Filter by user, institution, event_type, date range
- Pagination support
- Export capability for audit purposes
Web Controllers (AC: #2, #3)
- Create
app/controllers/cohorts/admin_controller.rb- Add authentication and role checks
- Implement institution list, new, create, show actions
- Add invite form handling
- Add routes to
config/routes.rb- Add API routes under
/api/v1/cohorts/ - Add web routes under
/cohorts/admin/
- Add API routes under
Frontend Components (AC: #2, #3)
- Create
app/javascript/cohorts/admin/InstitutionWizard.vue- Build form with all institution fields
- Add validation and error handling
- Connect to API client
- Add success/error notifications
- Create
app/javascript/cohorts/admin/AdminInviteModal.vue- Build invite form with role selection
- Add email validation
- Connect to invitation API
- Create
app/javascript/cohorts/admin/InstitutionList.vue- Display institutions table
- Add navigation to institution details
- Update
app/javascript/api/client.js- Add institution API methods
- Add invitation API methods
Jobs & Mailers (AC: #2) - WINSTON'S ASYNCHRONOUS SECURITY
Invitation Job Architecture
-
Create
app/services/invitation_service.rbclass InvitationService def self.create_invitation(institution, email, role, created_by) # Rate limiting check pending = CohortAdminInvitation.where(email: email, institution: institution, used_at: nil) .where('expires_at > ?', Time.current) .count raise RateLimit::LimitApproached if pending >= 5 invitation = CohortAdminInvitation.create!( institution: institution, email: email, role: role, created_by: created_by ) # Async email delivery CohortAdminInvitationJob.perform_async(invitation.id) invitation end def self.accept_invitation(raw_token, accepting_user) invitation = CohortAdminInvitation.active.find_by(token_preview: raw_token[0..8] + '...') return nil unless invitation return nil unless invitation.email == accepting_user.email return nil unless invitation.valid_token?(raw_token) # Atomic transaction AccountAccess.create!( account: invitation.institution.account, user: accepting_user, institution: invitation.institution, role: invitation.role ) invitation end end -
Create
app/jobs/cohort_admin_invitation_job.rbinclude Sidekiq::Jobsidekiq_options queue: :mailers, retry: 5- Check invitation validity before sending
- Handle email delivery failures gracefully
- Log security event on successful delivery
Mailer Architecture
-
Create
app/mailers/cohort_mailer.rb- Inherits from
ApplicationMailer - CRITICAL: Never include raw token in logs
- CRITICAL: Use HTTPS invitation URL
- CRITICAL: Token only in email, not in URL params
- Methods:
admin_invitation(invitation)- sends invitation emailsuper_admin_demoted(user, institution)- security notification
- Inherits from
-
Email Templates (
app/views/cohort_mailer/)admin_invitation.html.erb- HTML versionadmin_invitation.text.erb- Plain text version- Security: Include institution name, role, expiration
- CTA: Direct link to acceptance flow
- Warning: "Do not forward" disclaimer
Additional Jobs
-
Create
app/jobs/security_alert_job.rb- Send immediate alerts for critical events
- Queue:
critical_security - Retry: 3 attempts max
-
Create
app/jobs/invitation_cleanup_job.rb- Daily job to remove expired invitations
- Queue:
default - Retain for 1 week after expiration for audit
Authorization & Security (AC: #4, #5) - WINSTON'S DEFENSE-IN-DEPTH
4-Layer Security Architecture (MANDATORY)
Layer 1: Database-Level Security
- Foreign Key Constraints: All relationships must have FK constraints
- Unique Indexes:
[user_id, institution_id]on account_access - Scoped Queries: ALL database queries use
Institution.for_user(user) - Non-nullable: institution_id on account_access after backfill
- Test: Verify SQL injection attempts fail
Layer 2: Model-Level Security
- Update
lib/ability.rb(Cancancan)# Add to Ability.initialize if user.cohort_super_admin? can :manage, Institution, id: user.managed_institutions.select(:id) can :manage, Cohort, institution_id: user.managed_institutions.select(:id) can :manage, Sponsor, institution_id: user.managed_institutions.select(:id) can :manage, CohortAdminInvitation, institution_id: user.managed_institutions.select(:id) end if user.cohort_admin? can :read, Institution, id: user.institutions.select(:id) can :manage, Cohort, institution_id: user.institutions.select(:id) can :read, Sponsor, institution_id: user.institutions.select(:id) end - User Model Methods: Add
can_access_institution?,cohort_super_admin?,cohort_admin? - Institution Model Methods: Add
accessible_by?(user)verification - Account Access Validation:
validates :user_id, uniqueness: { scope: :institution_id }
Layer 3: Controller-Level Security
- API Base Controller Extensions:
verify_institution_access- checks institution_id parameterverify_institution_role(role)- checks role + institutionlog_security_event(type, details)- audit logging
- All Controllers: Add
before_action :verify_institution_accesswhere appropriate - Strong Parameters: All controllers use strong params with validation
- Error Handling: 403 Forbidden for unauthorized access, log all attempts
Layer 4: UI-Level Security
- Vue Route Guards: Prevent manual URL navigation to unauthorized paths
- API Client Validation: Validate institution_id before sending requests
- Context Storage: Store institution context in Vuex, validate server-side
- Role-Based UI: Show/hide elements based on
cohort_super_admin?/cohort_admin?
Token Security Architecture (CRITICAL)
Cryptographic Token System:
- Token Generation:
SecureRandom.urlsafe_base64(64)- 512 bits entropy - Storage: SHA-256 hash stored in database, never plaintext
- Preview: Store first 8 chars + '...' for debugging only
- Single-Use: Redis-backed enforcement with atomic operations
- Expiration: 24-hour default, configurable per invitation
- Rate Limiting: Max 5 pending invitations per email per institution
Redis Enforcement:
- Setup: Configure Redis connection in
config/initializers/redis.rb - Atomic Operations: Use
SET key value NX EX 86400for single-use - Race Condition Prevention: Concurrent requests handled correctly
- TTL Management: Automatic cleanup after expiration
Invitation Flow Security:
- Email Verification: Token only valid for matching email address
- Domain Validation: Optional email domain verification for institutions
- Acceptance Controller: One-time use, immediate invalidation
- AccountAccess Creation: Atomic transaction, rollback on failure
Security Event Logging & Monitoring
Security Event Model:
- Create
app/models/security_event.rbclass SecurityEvent < ApplicationRecord belongs_to :user, optional: true validates :event_type, presence: true validates :ip_address, presence: true def self.log(event_type, user = nil, details = {}) create!( user: user, event_type: event_type, ip_address: details[:ip_address] || '0.0.0.0', details: details.except(:ip_address) ) end end
Event Types to Log:
:unauthorized_institution_access- Cross-institution access attempt:insufficient_privileges- Role-based access denied:token_validation_failure- Invalid/expired token attempt:rate_limit_exceeded- Too many invitation attempts:invitation_accepted- Successful admin invitation acceptance:super_admin_demoted- Role change that invalidates tokens
Monitoring & Alerting:
- Alert Thresholds:
-
5 unauthorized access attempts/hour → Security alert
-
20 token validation failures/hour → Potential attack
- Any super_admin demotion → Immediate notification
-
- Dashboard: Security events view for cohort_super_admin
- Export: CSV/PDF export for audit compliance
Integration with Existing DocuSeal Security
Authentication Compatibility:
- Devise + JWT: No changes to existing auth system
- 2FA Support: Existing 2FA continues to work
- API Tokens: Existing access tokens unaffected
- Session Management: No changes to session handling
Authorization Compatibility:
- Existing Abilities: All existing CanCanCan abilities preserved
- Additive Only: New roles added to existing enum
- Account Isolation: Existing account-level isolation maintained
- Template/Submission Access: Existing permissions unchanged
Data Isolation Compatibility:
- Account-Level: Existing
account_idisolation preserved - Institution-Level: New
institution_idisolation within accounts - Combined Scopes: Queries must satisfy both levels
- Backward Compatibility: Existing data remains accessible
Security Testing Requirements
Unit Tests:
- Model Scopes:
Institution.for_user(user)returns correct records - User Methods:
can_access_institution?, role checks work correctly - Token Security: Hash generation, validation, single-use enforcement
- Rate Limiting: Max 5 invitations per email enforced
- Validation: Uniqueness constraints prevent duplicates
Request Tests:
- Cross-Institution Access: All endpoints tested with wrong institution_id
- Role-Based Access: Each role tested for allowed/denied actions
- Token Security: Reuse, expiration, wrong email scenarios
- Security Events: All violations logged correctly
- Rate Limiting: 429 responses when limit exceeded
Integration Tests:
- Complete Invitation Flow: Create → Email → Accept → Access
- Concurrent Access: Multiple users from different institutions
- Race Conditions: Token validation under load
- Rollback Scenarios: Migration rollback preserves security
Security Audit:
- Penetration Testing: Attempt cross-institution data access
- Token Analysis: Verify cryptographic strength
- Redis Security: Verify atomic operations prevent race conditions
- OWASP Check: Review for common vulnerabilities
Deployment Security Checklist
Pre-Production:
- Security Audit: Third-party review of token system
- Penetration Test: Attempt to breach data isolation
- Performance Test: Verify security doesn't degrade performance >10%
- Rollback Test: Verify security events persist after rollback
Production Monitoring:
- Security Dashboard: Real-time event monitoring
- Alert System: Immediate notification of violations
- Audit Trail: All events retained for compliance
- Incident Response: Documented procedure for security breaches
Emergency Procedures:
- Token Revocation: Ability to invalidate all pending invitations
- Access Lockdown: Emergency role removal capability
- Data Breach Protocol: Steps if isolation is compromised
- Recovery Plan: Restore security without data loss
Integration Verification - WINSTON'S COMPREHENSIVE TESTING
IV1: Existing DocuSeal Authentication Compatibility
- Test: Existing user login flows work unchanged
- Test: JWT tokens work for legacy endpoints
- Test: 2FA continues to function normally
- Test: API access tokens unaffected
- Test: Session management unchanged
IV2: Role System Compatibility
- Test: Existing DocuSeal roles (member, admin) unaffected
- Test: New roles (cohort_admin, cohort_super_admin) are additive only
- Test: No conflicts between old and new role enums
- Test: Existing template/submission access unchanged
- Test: Account-level isolation preserved
IV3: Performance Impact
- Benchmark: Existing user operations before/after
- Target: <10% performance degradation on existing flows
- Test: Query performance with 1000+ institutions
- Test: Concurrent user load (100+ simultaneous)
- Test: Database query optimization (EXPLAIN ANALYZE)
IV4: New Architecture Security (WINSTON'S MANDATORY)
- Test: 4-layer isolation with malicious inputs
- Attempt cross-institution access via altered institution_id
- Test SQL injection attempts on scoped queries
- Verify 403 responses for all unauthorized attempts
- Test: Redis token enforcement under concurrent load
- 50 concurrent token validation attempts
- Race condition prevention verified
- Single-use enforcement works correctly
- Test: Security event logging captures all violations
- All 6 event types logged correctly
- IP address captured accurately
- Details JSON contains relevant information
- Test: Rate limiting prevents invitation spam
- 6th invitation attempt returns 429
- Counter resets correctly
- Per-email limit enforced per institution
- Test: Token security scenarios
- Token reuse attempts fail
- Expired tokens rejected
- Wrong email address rejected
- Concurrent same-token validation handled correctly
IV5: Integration with Existing Features
- Test: Template sharing works with new institutions
- Test: Submission workflows integrate correctly
- Test: Webhook delivery unaffected
- Test: Email notifications work for new roles
- Test: Export functionality includes new data
Testing
- Model Tests:
spec/models/institution_spec.rb- Validations
- Associations
- Scopes (for_user)
- Data isolation
- Request Tests:
spec/requests/api/v1/institutions_spec.rb- Authentication requirements
- Authorization (super_admin only)
- Success/failure scenarios
- Error responses
- System Tests:
spec/system/institution_management_spec.rb- Institution creation flow
- Admin invitation flow
- Data isolation verification
- Role-based access control
Dev Notes
Overview
This is the foundation story for the entire 3-portal cohort management system. All subsequent stories depend on the institution and admin management infrastructure created here.
Key Integration Points
Existing DocuSeal Systems to Reference:
app/models/user.rb- Authentication patterns (Devise + 2FA)app/models/account.rb- Multi-tenancy structureapp/models/account_access.rb- Role-based permissions (extend this)app/controllers/api/api_base_controller.rb- API authenticationlib/ability.rb- Cancancan authorization rulesapp/javascript/template_builder/- Form builder integration patterns
Critical Files for Implementation:
config/routes.rb- Add cohort management routesapp/javascript/api/client.js- Add API methodsapp/views/mailers/- Email templates location
Winston's Architecture Requirements (MANDATORY)
4-Layer Data Isolation Foundation
This is the cornerstone of the entire architecture. Without this, the story cannot proceed safely.
Layer 1: Database Constraints
account_access.institution_idwith FK constraint- Unique index
[user_id, institution_id] - Non-nullable after backfill
Layer 2: Model Scopes
Institution.for_user(user)- used in ALL queriesUser.can_access_institution?(institution)- verification methodInstitution.accessible_by?(user)- security check
Layer 3: Controller Authorization
verify_institution_accessbefore_actionverify_institution_role(role)for role checkslog_security_eventfor audit trail
Layer 4: UI Validation
- Vue route guards
- API client pre-validation
- Context-based access control
Cryptographic Token Security
- Generation:
SecureRandom.urlsafe_base64(64)(512 bits) - Storage: SHA-256 hash only, never plaintext
- Enforcement: Redis-backed single-use with atomic operations
- Expiration: 24-hour default, strict validation
Security Event Logging
- Model:
SecurityEventwith user, event_type, ip_address, details - Events: 6 types covering all security violations
- Monitoring: Real-time alerts for critical thresholds
- Audit: Export capability for compliance
Integration Compatibility
- Additive Only: No changes to existing DocuSeal schemas
- Authentication: Devise + JWT unchanged
- Authorization: Existing CanCanCan abilities preserved
- Performance: <10% degradation on existing operations
Implementation Sequence (CRITICAL)
- ✅ Database migrations (institution_id on account_access)
- ✅ Model layer (4-layer isolation foundation)
- ✅ Security layer (token system + event logging)
- ✅ Controller layer (authorization + validation)
- ✅ UI layer (route guards + context management)
- ✅ Testing (comprehensive security tests)
- ✅ Features (invitation flow, CRUD operations)
⚠️ DO NOT implement features before security foundation is complete and tested.
Technical Constraints (from Architecture)
Must Follow:
- Ruby 3.4.2, Rails 7.x exact versions
- Vue 3.3.2 with Composition API (
<script setup>) - TailwindCSS 3.4.17 (no DaisyUI for new portals)
- Existing RuboCop and ESLint configurations
- Additive database changes only (no modifications to existing tables)
- Follow existing naming conventions and patterns
Integration Requirements:
- Authentication: Extend existing Devise + JWT without modification
- Database: Additive schema changes only, maintain 100% backward compatibility
- API: Extend existing v1 endpoints, follow RESTful patterns
- Authorization: Use existing Cancancan, extend abilities
- Email: Leverage existing DocuSeal email infrastructure via Sidekiq
Data Model Integration
New Tables:
institutions
- account_id (FK to accounts)
- name, registration_number, address
- contact_email, contact_phone
- super_admin_id (FK to users)
- settings (jsonb)
- timestamps
cohort_admin_invitations
- email, token, role
- institution_id (FK to institutions)
- sent_at, expires_at
- timestamps
Modified Tables:
# account_access - extend enum
enum role: {
# Existing
member: 'member',
admin: 'admin',
# New
cohort_admin: 'cohort_admin',
cohort_super_admin: 'cohort_super_admin'
}
Foreign Key Relationships:
- institutions.account_id → accounts.id
- institutions.super_admin_id → users.id
- cohort_admin_invitations.institution_id → institutions.id
- account_access.account_id → accounts.id (existing)
Performance Considerations
Expected Query Patterns:
Institution.for_user(user)- scoped queryAccountAccess.cohort_admins- role filteringuser.institutions- association access
Index Strategy:
- institutions.account_id (unique)
- institutions.registration_number (unique)
- institutions.super_admin_id
- cohort_admin_invitations.token (unique, for lookup)
- account_access.role (for role queries)
Security Requirements
Data Isolation:
- All queries must be scoped to current user's institution
- API endpoints must enforce institution-based authorization
- Cross-institution access attempts logged as security events
- Token-based invitations with expiration (24 hours recommended)
Role Enforcement:
- Super admins: institution creation, admin management, all cohorts
- Regular admins: cohort management within their institution only
- Unauthorized access returns 403 Forbidden
Testing Standards
Test File Locations:
- Models:
spec/models/ - Request/API:
spec/requests/api/v1/ - System/Feature:
spec/system/ - Jobs:
spec/jobs/ - Mailers:
spec/mailers/
Test Frameworks:
- RSpec for all tests
- FactoryBot for test data
- Capybara for system tests
- Shoulda matchers for model validations
Test Requirements:
- 80% minimum coverage on new code
- Test happy paths and error scenarios
- Test authorization at all levels
- Test data isolation thoroughly
- Include performance tests for large datasets
Specific Test Scenarios:
- Authentication: Existing DocuSeal users unaffected
- Authorization: Role-based access enforced
- Data Isolation: No cross-institution access
- Validation: All business rules enforced
- Integration: Works with existing DocuSeal features
Rollback Strategy
Database:
# Rollback migrations
bin/rails db:rollback STEP=2
# Migrations: institutions + cohort_admin_invitations
Code:
# Git revert
git revert <commit-hash>
Routes:
- Remove cohort routes from
config/routes.rb
Verification:
- Run existing test suite to ensure no impact
- Verify existing DocuSeal features still work
Feature Flag (Optional):
# config/initializers/features.rb
Docuseal.enable_cohort_management = ENV['ENABLE_COHORT_MANAGEMENT'] == 'true'
# In controllers:
before_action :require_cohort_feature!
def require_cohort_feature!
redirect_to root_path unless Docuseal.enable_cohort_management?
end
Dependencies & Sequencing
This Story Blocks:
- Story 1.2 (Cohort Creation) - requires institutions
- Story 1.3 (Student Enrollment) - requires institutions
- Story 1.4 (Admin Verification) - requires institutions
- All portal stories require admin role system
Can Run Parallel:
- Story 1.8 (Notifications) - can start once email templates ready
- Story 1.10 (State Management) - can design while this is implemented
Critical Success Factors
- Data Isolation Must Be Perfect - Any breach here is a security issue
- Role System Must Be Extensible - Future roles may be added
- Performance Cannot Degrade - Existing features must remain fast
- Integration Must Be Seamless - No breaking changes to DocuSeal
- Rollback Must Be Clean - Must be able to revert without data loss
Winston's Critical Success Factors
- 4-Layer Isolation Implementation - Database → Model → Controller → UI
- Cryptographic Token Security - SHA-256 + Redis + atomic operations
- Comprehensive Security Logging - All violations captured and alerted
- Zero Impact on Existing Features - IV1-IV5 tests must all pass
- Security-First Implementation Sequence - Foundation before features
- Developer Understanding - Team must understand 4-layer architecture
- Production Monitoring - Real-time security event dashboard
Implementation Phases (Winston's Recommendation)
Phase 1: Foundation (Week 1)
- Database migrations (institution_id on account_access)
- Model layer with 4-layer isolation scopes
- Basic ability rules and user extensions
Phase 2: Security Core (Week 2)
- Token generation and validation system
- Redis single-use enforcement
- Security event model and logging
- API base controller extensions
Phase 3: Controllers & Services (Week 3)
- Institution and invitation controllers
- InvitationService for business logic
- Mailer jobs and email templates
- Rate limiting implementation
Phase 4: Testing & Validation (Week 4)
- Comprehensive security tests (IV4 mandatory)
- Penetration testing of data isolation
- Performance benchmarking
- Security audit review
Phase 5: Features & UI (Week 5)
- Frontend components (only after security passes)
- Integration with existing DocuSeal features
- Final integration verification (IV1-IV3, IV5)
⚠️ NO feature work until Phase 4 security tests pass
Known Risks & Mitigations
Risk: Authentication conflicts between new and existing roles Mitigation: Extensive testing of login flows, JWT token compatibility
Risk: Performance impact on existing user operations Mitigation: Benchmark before/after, add indexes, optimize queries
Risk: Data isolation bugs leading to cross-institution access Mitigation: Thorough testing, security audit, logging of unauthorized attempts
Risk: Migration failures on production data Mitigation: Test on production-like data, have rollback plan ready
Testing Standards
Location: All tests in spec/ directory following existing patterns
Framework: RSpec + FactoryBot + Capybara
Coverage: 80% minimum on new code
Patterns: Follow existing test helpers and matchers
Integration: Must pass existing DocuSeal test suite
Change Log
| Date | Version | Description | Author |
|---|---|---|---|
| 2025-01-03 | 1.0 | Initial story creation following BMAD template | Bob (Scrum Master) |
Dev Agent Record
This section will be populated by the development agent during implementation
Agent Model Used
To be filled by dev agent
Debug Log References
To be filled by dev agent
Completion Notes List
To be filled by dev agent
File List
To be filled by dev agent
QA Results
Reviewer: Quinn (Test Architect) Architect: Winston (System Architect) Review Date: 2025-01-03 Risk Assessment: docs/qa/assessments/1.1.institution-admin-risk-20250103.md Quality Gate: docs/qa/gates/1.1.institution-admin.yml
Overall Assessment: ⚠️ CONDITIONAL PASS (Implementation-Dependent)
Updated Risk Score: 18/100 (Medium Risk) → Architecturally Mitigated Critical Risks: 2 → Architecturally Addressed (reduced to Medium with proper implementation) High Risks: 4 → Require Execution Excellence
Architectural Review Status: ✅ COMPLETE
Winston's 4-Layer Security Architecture ✅
- Layer 1: Database-Level - Foreign keys, unique indexes, non-nullable constraints
- Layer 2: Model-Level -
Institution.for_user(user)scopes, security methods - Layer 3: Controller-Level -
verify_institution_accessbefore_action, authorization - Layer 4: UI-Level - Vue route guards, API client pre-validation
Cryptographic Token System ✅
- Generation:
SecureRandom.urlsafe_base64(64)- 512 bits entropy - Storage: SHA-256 hash only,
token_previewfor debugging - Enforcement: Redis
SET key NX EX 86400for atomic single-use - Validation: Email matching, expiration, hash verification
Security Event Logging ✅
- Model:
SecurityEventwith user, event_type, ip_address, details - Events: 6 types defined (unauthorized access, insufficient privileges, token failures, rate limits, acceptance, demotion)
- Monitoring: Alert thresholds configured (>5 unauthorized/hour, >20 token failures/hour)
Risk Profile Update: From High → Medium Risk
✅ Critical Risks Now Architecturally Mitigated
| Risk | Before | After Winston's Architecture | New Status |
|---|---|---|---|
| SEC-001 Cross-institution access | 9 (Critical) | 4-layer defense-in-depth | ✅ Mitigated → 3* |
| SEC-002 Token security flaws | 9 (Critical) | SHA-256 + Redis + atomic ops | ✅ Mitigated → 3* |
*Reduced score = Architecture reduces probability/impact when implemented correctly
⚠️ High Risks Requiring Execution Excellence
| Risk | Score | Challenge | Mitigation | Testing Required |
|---|---|---|---|---|
| SEC-003 Role authorization bypass | 6 | Complex dual-role system | Cancancan + controller checks | Role escalation attempts |
| DATA-001 Migration rollback | 6 | Zero-downtime complexity | Winston's backfill pattern | Production-like data testing |
| PERF-001 Performance degradation | 6 | 10% threshold is strict | Indexing + query optimization | Benchmark before/after |
| TECH-001 Integration conflicts | 6 | Additive changes only | IV1-IV5 integration tests | Existing test suite |
🔍 Medium Risks - Implementation Details
| Risk | Score | Winston's Specification | Implementation Gap |
|---|---|---|---|
| OPS-001 Security logging | 4 | 6 event types + alerts | Real-time alert system |
| PERF-002 Rate limiting | 4 | 5 invites/email/inst | Concurrent edge cases |
| TECH-002 Email delivery | 4 | Sidekiq retry logic | Failure handling |
| DATA-002 Token cleanup | 4 | Daily job, 1-week retention | Implementation needed |
Updated Gate Decision: ⚠️ CONDITIONAL PASS (Improved)
Status: READY FOR DEVELOPMENT → READY FOR PRODUCTION (with conditions)
Key Change: Story evolved from "high risk design" to "architecturally sound but execution-critical"
Implementation Phases (MANDATORY)
Phase 1: Foundation (Week 1) ✅ Ready
- Database migrations:
institution_idonaccount_access - Model layer: 4-layer isolation scopes
- Basic ability rules
Phase 2: Security Core (Week 2) ✅ Ready
- Token system: SHA-256 + Redis enforcement
- Security events: 6 types + logging
- API base controller extensions
Phase 3: Controllers & Services (Week 3) ✅ Ready
- Institution & invitation controllers
InvitationServicewith rate limiting- Mailer jobs + email templates
Phase 4: Testing & Validation (Week 4-5) ⚠️ NON-NEGOTIABLE
- IV4 Security Tests (Critical):
- Cross-institution access attempts (all endpoints)
- Token reuse/expiration/race conditions (50 concurrent)
- Role authorization bypass tests
- SQL injection on scoped queries
- Performance benchmarks (<10% degradation)
- Migration rollback verification
- Existing DocuSeal test suite (IV1-IV3, IV5)
Phase 5: Features & UI (Week 5+) ⚠️ ONLY AFTER PHASE 4 PASSES
- Frontend components
- Integration with existing features
- Security audit review
Prerequisites for Production
MUST PASS Phase 4 Testing:
- IV4 Security Tests: All scenarios pass with malicious inputs
- Performance: <10% degradation on existing operations
- Integration: IV1-IV5 tests all pass
- Rollback: Verified on production-like data
MUST COMPLETE:
- Security Audit: Third-party review of token system
- Team Understanding: 4-layer architecture review with developers
- Redis Infrastructure: Properly configured for token enforcement
- Monitoring: Security event dashboard deployed
Critical Success Factors
- ✅ Architecture is Sound: Winston's 4-layer design addresses all critical risks
- ⚠️ Execution is Critical: Implementation must be perfect
- ⚠️ Testing is Extensive: IV4 security tests represent significant burden
- ⚠️ Performance is Strict: 10% degradation threshold is challenging
Remaining Risk: Implementation Execution
Primary concerns:
- Developer Understanding: Team must grasp 4-layer architecture conceptually
- Redis Infrastructure: Token enforcement requires proper Redis setup
- Performance Budget: 10% target may be hard to guarantee
- Test Complexity: IV4 tests require sophisticated concurrency/race condition testing
Recommendations
- ✅ APPROVED FOR DEVELOPMENT: Story is architecturally sound and comprehensive
- ⚠️ PHASE 4 IS MANDATORY: No shortcuts on security testing
- ⚠️ TEAM KICKOFF REQUIRED: Ensure understanding of 4-layer architecture
- ⚠️ PERFORMANCE BASELINE: Benchmark existing operations before changes
- ⚠️ SECURITY AUDIT: Third-party review before production deployment
Next Steps
- ✅ Story Ready: Winston's architecture complete and documented
- ✅ QA Updated: Risk profile reflects architectural improvements
- ⏳ Team Kickoff: Review 4-layer architecture + Phase 1 start
- ⏳ Phase 1-3: Implement foundation, security core, controllers
- ⏳ Phase 4 Testing: Comprehensive IV4 security tests (CRITICAL)
- ⏳ Final Gate: Quinn's production approval after Phase 4 passes
Note: Story evolved from HIGH RISK to ARCHITECTURALLY MITIGATED thanks to Winston's comprehensive design. The primary remaining risk is execution excellence - the 4-layer architecture must be implemented perfectly, and IV4 security tests must pass completely before production. This is now a high-complexity, security-critical implementation requiring disciplined execution.
Story Status: ✅ APPROVED FOR DEVELOPMENT (Architecture Complete) Production Gate: ⚠️ CONDITIONAL (Pending Phase 4 Security Tests) Next Action: Team kickoff → Phase 1 implementation → Phase 4 security validation