You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuseal/docs/stories/1.1.institution-admin.md

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

  1. Database schema for institutions and admin roles exists
  2. Super admins can create institutions and invite other admins
  3. Regular admins can manage cohorts within their institution
  4. Admins cannot access other institutions' data
  5. 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_id reference to account_accesses table (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)
  • 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_preview for debugging (first 8 chars + '...')

    • CRITICAL METHODS:

      • def generate_token - uses SecureRandom.urlsafe_base64(64)
      • def valid_token?(raw_token) - validates hash + Redis single-use
      • def expired? - checks expires_at
      • def 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_access before_action
      def 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

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 account
      • update/destroy - requires cohort_super_admin role + ownership
      • index/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

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 limit
      • index - scoped to institution, cohort_admin+ access
      • revoke - cohort_super_admin only, mark used_at
    • CRITICAL: Use InvitationService for 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 AccountAccess record 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/

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.rb

    class 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.rb

    • include Sidekiq::Job
    • sidekiq_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 email
      • super_admin_demoted(user, institution) - security notification
  • Email Templates (app/views/cohort_mailer/)

    • admin_invitation.html.erb - HTML version
    • admin_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 parameter
    • verify_institution_role(role) - checks role + institution
    • log_security_event(type, details) - audit logging
  • All Controllers: Add before_action :verify_institution_access where 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 86400 for 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.rb
    class 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_id isolation preserved
  • Institution-Level: New institution_id isolation 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 structure
  • app/models/account_access.rb - Role-based permissions (extend this)
  • app/controllers/api/api_base_controller.rb - API authentication
  • lib/ability.rb - Cancancan authorization rules
  • app/javascript/template_builder/ - Form builder integration patterns

Critical Files for Implementation:

  • config/routes.rb - Add cohort management routes
  • app/javascript/api/client.js - Add API methods
  • app/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_id with FK constraint
  • Unique index [user_id, institution_id]
  • Non-nullable after backfill

Layer 2: Model Scopes

  • Institution.for_user(user) - used in ALL queries
  • User.can_access_institution?(institution) - verification method
  • Institution.accessible_by?(user) - security check

Layer 3: Controller Authorization

  • verify_institution_access before_action
  • verify_institution_role(role) for role checks
  • log_security_event for 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: SecurityEvent with 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)

  1. Database migrations (institution_id on account_access)
  2. Model layer (4-layer isolation foundation)
  3. Security layer (token system + event logging)
  4. Controller layer (authorization + validation)
  5. UI layer (route guards + context management)
  6. Testing (comprehensive security tests)
  7. 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 query
  • AccountAccess.cohort_admins - role filtering
  • user.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:

  1. Authentication: Existing DocuSeal users unaffected
  2. Authorization: Role-based access enforced
  3. Data Isolation: No cross-institution access
  4. Validation: All business rules enforced
  5. 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

  1. Data Isolation Must Be Perfect - Any breach here is a security issue
  2. Role System Must Be Extensible - Future roles may be added
  3. Performance Cannot Degrade - Existing features must remain fast
  4. Integration Must Be Seamless - No breaking changes to DocuSeal
  5. Rollback Must Be Clean - Must be able to revert without data loss

Winston's Critical Success Factors

  1. 4-Layer Isolation Implementation - Database → Model → Controller → UI
  2. Cryptographic Token Security - SHA-256 + Redis + atomic operations
  3. Comprehensive Security Logging - All violations captured and alerted
  4. Zero Impact on Existing Features - IV1-IV5 tests must all pass
  5. Security-First Implementation Sequence - Foundation before features
  6. Developer Understanding - Team must understand 4-layer architecture
  7. 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_access before_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_preview for debugging
  • Enforcement: Redis SET key NX EX 86400 for atomic single-use
  • Validation: Email matching, expiration, hash verification

Security Event Logging

  • Model: SecurityEvent with 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 DEVELOPMENTREADY 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_id on account_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
  • InvitationService with 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

  1. Architecture is Sound: Winston's 4-layer design addresses all critical risks
  2. ⚠️ Execution is Critical: Implementation must be perfect
  3. ⚠️ Testing is Extensive: IV4 security tests represent significant burden
  4. ⚠️ 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

  1. APPROVED FOR DEVELOPMENT: Story is architecturally sound and comprehensive
  2. ⚠️ PHASE 4 IS MANDATORY: No shortcuts on security testing
  3. ⚠️ TEAM KICKOFF REQUIRED: Ensure understanding of 4-layer architecture
  4. ⚠️ PERFORMANCE BASELINE: Benchmark existing operations before changes
  5. ⚠️ SECURITY AUDIT: Third-party review before production deployment

Next Steps

  1. Story Ready: Winston's architecture complete and documented
  2. QA Updated: Risk profile reflects architectural improvements
  3. Team Kickoff: Review 4-layer architecture + Phase 1 start
  4. Phase 1-3: Implement foundation, security core, controllers
  5. Phase 4 Testing: Comprehensive IV4 security tests (CRITICAL)
  6. 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