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

1011 lines
41 KiB

# <!-- Powered by BMAD™ Core -->
## 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
```ruby
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:
```ruby
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`**
```ruby
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)**
```ruby
# 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`**
```ruby
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:**
```sql
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:**
```ruby
# 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:**
```bash
# Rollback migrations
bin/rails db:rollback STEP=2
# Migrations: institutions + cohort_admin_invitations
```
**Code:**
```bash
# 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):**
```ruby
# 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](../qa/assessments/1.1.institution-admin-risk-20250103.md)
**Quality Gate:** [docs/qa/gates/1.1.institution-admin.yml](../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 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_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