mirror of https://github.com/docusealco/docuseal
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.
81 lines
2.3 KiB
81 lines
2.3 KiB
# frozen_string_literal: true
|
|
|
|
# CohortMailer
|
|
# Handles email delivery for cohort management
|
|
# Implements Winston's secure email requirements
|
|
class CohortMailer < ApplicationMailer
|
|
# CRITICAL: Never include raw token in logs
|
|
# CRITICAL: Use HTTPS invitation URL
|
|
# CRITICAL: Token only in email, not in URL params
|
|
|
|
def admin_invitation(invitation)
|
|
@invitation = invitation
|
|
@institution = invitation.institution
|
|
@role = invitation.role
|
|
@token = generate_acceptance_token(invitation)
|
|
|
|
# Security: Verify we have the token before sending
|
|
unless @token.present?
|
|
Rails.logger.error "No token available for invitation #{invitation.id}"
|
|
return
|
|
end
|
|
|
|
# Set email metadata
|
|
@subject = "Invitation to manage #{@institution.name} - #{@role.titleize}"
|
|
|
|
# Track email delivery
|
|
mail(
|
|
to: invitation.email,
|
|
subject: @subject,
|
|
track: true
|
|
) do |format|
|
|
format.html { render 'admin_invitation' }
|
|
format.text { render 'admin_invitation' }
|
|
end
|
|
end
|
|
|
|
def super_admin_demoted(user, institution)
|
|
@user = user
|
|
@institution = institution
|
|
|
|
mail(
|
|
to: user.email,
|
|
subject: "Role change notification - #{institution.name}"
|
|
)
|
|
end
|
|
|
|
private
|
|
|
|
# Generate secure acceptance token
|
|
# Note: This retrieves the raw token from Redis if available
|
|
# If not available (already used), returns nil
|
|
def generate_acceptance_token(invitation)
|
|
# The raw token is only available during the initial creation
|
|
# After that, we rely on the invitation flow
|
|
# For security, we don't regenerate tokens
|
|
|
|
# In a real implementation, we might store the raw token temporarily
|
|
# in Redis with a short TTL for email delivery
|
|
redis = Redis.current
|
|
key = "invitation_token_pending:#{invitation.hashed_token}"
|
|
|
|
# Try to get pending token (set during creation)
|
|
raw_token = redis.get(key)
|
|
|
|
# If not found, the invitation might be old or token already delivered
|
|
# In this case, we should not send the email
|
|
return nil unless raw_token
|
|
|
|
# Set TTL for the token in email (24 hours from now)
|
|
redis.expire(key, 86400)
|
|
|
|
raw_token
|
|
end
|
|
|
|
# Override to add security headers
|
|
def mail(headers = {}, &block)
|
|
headers[:'X-SECURITY'] = 'Cohort-Invitation'
|
|
headers[:'X-Role'] = @role if @role
|
|
super(headers, &block)
|
|
end
|
|
end |