Ok - this time add the new controllers we stripped out of inline and add back the csp
This commit is contained in:
24
app/javascript/controllers/application_form_controller.js
Normal file
24
app/javascript/controllers/application_form_controller.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["appTypeSelect", "oidcFields", "forwardAuthFields"]
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.updateFieldVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFieldVisibility() {
|
||||||
|
const appType = this.appTypeSelectTarget.value
|
||||||
|
|
||||||
|
if (appType === 'oidc') {
|
||||||
|
this.oidcFieldsTarget.classList.remove('hidden')
|
||||||
|
this.forwardAuthFieldsTarget.classList.add('hidden')
|
||||||
|
} else if (appType === 'forward_auth') {
|
||||||
|
this.oidcFieldsTarget.classList.add('hidden')
|
||||||
|
this.forwardAuthFieldsTarget.classList.remove('hidden')
|
||||||
|
} else {
|
||||||
|
this.oidcFieldsTarget.classList.add('hidden')
|
||||||
|
this.forwardAuthFieldsTarget.classList.add('hidden')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/javascript/controllers/backup_codes_controller.js
Normal file
28
app/javascript/controllers/backup_codes_controller.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static values = {
|
||||||
|
codes: Array
|
||||||
|
}
|
||||||
|
|
||||||
|
download() {
|
||||||
|
const content = "Clinch Backup Codes\n" +
|
||||||
|
"===================\n\n" +
|
||||||
|
this.codesValue.join("\n") +
|
||||||
|
"\n\nSave these codes in a secure location."
|
||||||
|
|
||||||
|
const blob = new Blob([content], { type: 'text/plain' })
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = 'clinch-backup-codes.txt'
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
document.body.removeChild(a)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
print() {
|
||||||
|
window.print()
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/javascript/controllers/mobile_sidebar_controller.js
Normal file
21
app/javascript/controllers/mobile_sidebar_controller.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus";
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["sidebarOverlay", "button"];
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
// Initialize mobile sidebar functionality
|
||||||
|
}
|
||||||
|
|
||||||
|
openSidebar() {
|
||||||
|
if (this.hasSidebarOverlayTarget) {
|
||||||
|
this.sidebarOverlayTarget.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSidebar() {
|
||||||
|
if (this.hasSidebarOverlayTarget) {
|
||||||
|
this.sidebarOverlayTarget.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
config/initializers/content_security_policy.rb
Normal file
64
config/initializers/content_security_policy.rb
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
|
# Define an application-wide content security policy.
|
||||||
|
# See the Securing Rails Applications Guide for more information:
|
||||||
|
# https://guides.rubyonrails.org/security.html#content-security-policy-header
|
||||||
|
|
||||||
|
Rails.application.configure do
|
||||||
|
config.content_security_policy do |policy|
|
||||||
|
# Default to self for everything, plus blob: for file downloads
|
||||||
|
policy.default_src :self, "blob:"
|
||||||
|
|
||||||
|
# Scripts: Allow self, importmaps, unsafe-inline for Turbo/StimulusJS, and blob: for downloads
|
||||||
|
# Note: unsafe_inline is needed for Stimulus controllers and Turbo navigation
|
||||||
|
policy.script_src :self, :unsafe_inline, :unsafe_eval, "blob:"
|
||||||
|
|
||||||
|
# Styles: Allow self and unsafe_inline for TailwindCSS dynamic classes
|
||||||
|
# and Stimulus controller style manipulations
|
||||||
|
policy.style_src :self, :unsafe_inline
|
||||||
|
|
||||||
|
# Images: Allow self, data URLs, and https for external images
|
||||||
|
policy.img_src :self, :data, :https
|
||||||
|
|
||||||
|
# Fonts: Allow self and data URLs
|
||||||
|
policy.font_src :self, :data
|
||||||
|
|
||||||
|
# Connect: Allow self for API calls, WebAuthn, and ActionCable if needed
|
||||||
|
# WebAuthn endpoints are on the same domain, so self is sufficient
|
||||||
|
policy.connect_src :self, "wss:"
|
||||||
|
|
||||||
|
# Media: Allow self
|
||||||
|
policy.media_src :self
|
||||||
|
|
||||||
|
# Object and embed sources: Disallow for security (no Flash/etc)
|
||||||
|
policy.object_src :none
|
||||||
|
policy.frame_src :none
|
||||||
|
policy.frame_ancestors :none
|
||||||
|
|
||||||
|
# Base URI: Restricted to self
|
||||||
|
policy.base_uri :self
|
||||||
|
|
||||||
|
# Form actions: Allow self for all form submissions
|
||||||
|
policy.form_action :self
|
||||||
|
|
||||||
|
# Manifest sources: Allow self for PWA manifest
|
||||||
|
policy.manifest_src :self
|
||||||
|
|
||||||
|
# Worker sources: Allow self for potential Web Workers
|
||||||
|
policy.worker_src :self
|
||||||
|
|
||||||
|
# Child sources: Allow self for any future iframes
|
||||||
|
policy.child_src :self
|
||||||
|
|
||||||
|
# Additional security headers for WebAuthn
|
||||||
|
# Required for WebAuthn to work properly
|
||||||
|
policy.require_trusted_types_for :none
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start with CSP in report-only mode for testing
|
||||||
|
# Set to false after verifying everything works in production
|
||||||
|
config.content_security_policy_report_only = Rails.env.development?
|
||||||
|
|
||||||
|
# Report CSP violations (optional - uncomment to enable)
|
||||||
|
# config.content_security_policy_report_uri = "/csp-violations"
|
||||||
|
end
|
||||||
13
db/migrate/20251104061455_clear_existing_backup_codes.rb
Normal file
13
db/migrate/20251104061455_clear_existing_backup_codes.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
class ClearExistingBackupCodes < ActiveRecord::Migration[8.1]
|
||||||
|
def up
|
||||||
|
# Clear all existing backup codes to force regeneration with BCrypt hashing
|
||||||
|
# This is a security migration to move from plain text to hashed storage
|
||||||
|
User.where.not(backup_codes: nil).update_all(backup_codes: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# This migration cannot be safely reversed
|
||||||
|
# as the original plain text codes cannot be recovered
|
||||||
|
raise ActiveRecord::IrreversibleMigration
|
||||||
|
end
|
||||||
|
end
|
||||||
12
db/migrate/20251104064114_change_backup_codes_to_json.rb
Normal file
12
db/migrate/20251104064114_change_backup_codes_to_json.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class ChangeBackupCodesToJson < ActiveRecord::Migration[8.1]
|
||||||
|
def up
|
||||||
|
# Change the column type from text to json
|
||||||
|
# This will automatically handle JSON serialization/deserialization
|
||||||
|
change_column :users, :backup_codes, :json
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# Revert back to text if needed
|
||||||
|
change_column :users, :backup_codes, :text
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user