From 101c902dc04615fc720e8ed66ed64d9d15214319 Mon Sep 17 00:00:00 2001 From: Soumya Ray Date: Thu, 17 Jun 2021 16:26:30 +0800 Subject: [PATCH 1/2] Sign non-authenticated requests to API --- Gemfile | 1 + Rakefile | 10 ++++++++++ app/controllers/auth.rb | 8 ++++---- app/controllers/documents.rb | 1 + app/controllers/projects.rb | 1 + app/lib/signed_message.rb | 20 +++++++++++++++++++ app/presentation/assets/css/style.css | 5 +++++ app/presentation/views/document.slim | 2 +- .../views/document_new_partial.slim | 2 +- app/presentation/views/layout.slim | 9 +++++---- app/presentation/views/login.slim | 7 ++----- app/presentation/views/nav.slim | 2 +- app/presentation/views/project.slim | 2 +- app/presentation/views/projects_all.slim | 6 +++--- app/services/authenticate_account.rb | 10 +++++----- app/services/authorize_github_account.rb | 16 ++++++++------- app/services/create_account.rb | 6 ++---- app/services/verify_registration.rb | 7 +++++-- config/environments.rb | 6 +++++- config/secrets.example.yml | 5 +++++ spec/integration/service_authenticate_spec.rb | 4 ++-- 21 files changed, 89 insertions(+), 41 deletions(-) create mode 100644 app/lib/signed_message.rb diff --git a/Gemfile b/Gemfile index 5095f31..56fb0e5 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ gem 'redis-rack' gem 'dry-validation' gem 'rack-ssl-enforcer' gem 'rbnacl' # assumes libsodium package already installed +gem 'secure_headers' # Debugging gem 'pry' diff --git a/Rakefile b/Rakefile index 279eb8d..04cf77b 100644 --- a/Rakefile +++ b/Rakefile @@ -63,6 +63,16 @@ namespace :generate do end end +namespace :url do + # usage: $ rake url:integrity URL=http://example.org/script.js + desc 'Generate integrity hash for a URL' + task :integrity do + sha384 = `curl -L -s #{ENV['URL']} | openssl dgst -sha384 -binary | \ + openssl enc -base64` + puts "sha384-#{sha384}" + end +end + namespace :session do desc 'Wipe all sessions stored in Redis' task :wipe => :load_lib do diff --git a/app/controllers/auth.rb b/app/controllers/auth.rb index 1cc383b..6dfb3ee 100644 --- a/app/controllers/auth.rb +++ b/app/controllers/auth.rb @@ -15,7 +15,6 @@ def gh_oauth_url(config) end route('auth') do |routing| - @oauth_callback = '/auth/sso_callback' @login_route = '/auth/login' routing.is 'login' do # GET /auth/login @@ -34,7 +33,8 @@ def gh_oauth_url(config) routing.redirect @login_route end - authenticated = AuthenticateAccount.new.call(**credentials.values) + authenticated = AuthenticateAccount.new(App.config) + .call(**credentials.values) current_account = Account.new( authenticated[:account], @@ -46,9 +46,9 @@ def gh_oauth_url(config) flash[:notice] = "Welcome back #{current_account.username}!" routing.redirect '/projects' rescue AuthenticateAccount::NotAuthenticatedError - flash[:error] = 'Username and password did not match our records' + flash.now[:error] = 'Username and password did not match our records' response.status = 401 - routing.redirect @login_route + view :login rescue AuthenticateAccount::ApiServerError => e App.logger.warn "API server error: #{e.inspect}\n#{e.backtrace}" flash[:error] = 'Our servers are not responding -- please try later' diff --git a/app/controllers/documents.rb b/app/controllers/documents.rb index 3a2222f..d3cb5ef 100644 --- a/app/controllers/documents.rb +++ b/app/controllers/documents.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'roda' +require_relative './app' module Credence # Web controller for Credence API diff --git a/app/controllers/projects.rb b/app/controllers/projects.rb index a77d4fe..2fc7cfa 100644 --- a/app/controllers/projects.rb +++ b/app/controllers/projects.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'roda' +require_relative './app' module Credence # Web controller for Credence API diff --git a/app/lib/signed_message.rb b/app/lib/signed_message.rb new file mode 100644 index 0000000..06a42c5 --- /dev/null +++ b/app/lib/signed_message.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'base64' +require 'rbnacl' + +# Encrypt and Decrypt from Database +class SignedMessage + # Call setup once to pass in config variable with SIGNING_KEY attribute + def self.setup(config) + @signing_key = Base64.strict_decode64(config.SIGNING_KEY) + end + + def self.sign(message) + signature = RbNaCl::SigningKey.new(@signing_key) + .sign(message.to_json) + .then { |sig| Base64.strict_encode64(sig) } + + { data: message, signature: signature } + end +end diff --git a/app/presentation/assets/css/style.css b/app/presentation/assets/css/style.css index 47424c0..2901e73 100644 --- a/app/presentation/assets/css/style.css +++ b/app/presentation/assets/css/style.css @@ -6,4 +6,9 @@ .top-buffer { margin-top:20px; +} + +.logo { + max-width:35px; + margin-top: -7px; } \ No newline at end of file diff --git a/app/presentation/views/document.slim b/app/presentation/views/document.slim index 4f370b1..cd48337 100644 --- a/app/presentation/views/document.slim +++ b/app/presentation/views/document.slim @@ -14,5 +14,5 @@ dl dd class="blockquote" a data-bs-toggle="collapse" href="#document" aria-expanded="false" aria-controls="document" show document div id="document" class="collapse" - dov class="card card-body" + div class="card card-body" pre class="force-wrap" #{document.content} diff --git a/app/presentation/views/document_new_partial.slim b/app/presentation/views/document_new_partial.slim index c7fe207..9a16dce 100644 --- a/app/presentation/views/document_new_partial.slim +++ b/app/presentation/views/document_new_partial.slim @@ -11,7 +11,7 @@ div id="newDocumentModal" class="modal fade" role="dialog" input type='text' name='filename' id='filename_input' class="form-control" placeholder="Filename is required" div class="form-group" label for='relative_path_input' Relative File Path: - span class="small" style="margin-left:10px;" (relative to project root) + span class="small ms-2" (relative to project root) input type='text' name='relative_path' id='relative_path_input' class="form-control" placeholder="(optional)" value="/" div class="form-group" label for='description_input' Description: diff --git a/app/presentation/views/layout.slim b/app/presentation/views/layout.slim index e6c9061..57f6c2a 100644 --- a/app/presentation/views/layout.slim +++ b/app/presentation/views/layout.slim @@ -5,15 +5,16 @@ html / Themed Bootstrap CSS (Cerulean Theme) - see bootswatch.com for more themes / - default Bootstrap CSS: https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css - link rel="stylesheet" href="https://bootswatch.com/5/cerulean/bootstrap.min.css" - link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/FortAwesome/Font-Awesome@5.15.3/css/all.min.css" + link rel="shortcut icon" href="#" + link rel="stylesheet" href="https://bootswatch.com/5/cerulean/bootstrap.min.css" integrity="sha384-QIHD79FAZ7/24rhhTveEfwJuiAeoPTOQpcVTWsfuPqh3JhPaV6LW8H0cTshmT0Jn" crossorigin="anonymous" + link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/FortAwesome/Font-Awesome@5.15.3/css/all.min.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous" / Custom CSS == assets(:css) body / Popper & Bootstrap Javascript (Load in this order) - script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" - script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.min.js" + script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous" + script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.min.js" integrity="sha384-lpyLfhYuitXl2zRZ5Bn2fqnhNAKOAaM/0Kr9laMspuaMiZfGmfwRNFh8HlMy49eQ" crossorigin="anonymous" == render :nav div class='container' id='html_body' diff --git a/app/presentation/views/login.slim b/app/presentation/views/login.slim index 6ec4a5d..abd24de 100644 --- a/app/presentation/views/login.slim +++ b/app/presentation/views/login.slim @@ -1,7 +1,4 @@ / Fontawesome and Bootstrap-Social for Github buttons -link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/fontawesome.min.css" -link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/5.0.0/bootstrap-social.css" - div class="row" div class="col-sm-1" div class="col-sm-4" @@ -21,7 +18,7 @@ div class="row" .row h4.text-center Login with Github .row - a class="btn-block btn-social btn-lg btn-github" href=gh_oauth_url + a class="btn btn-dark btn-lg" href=gh_oauth_url span class="fab fa-github" - == "Github Sign-in" + == " Github Sign-in" .col-sm-1 \ No newline at end of file diff --git a/app/presentation/views/nav.slim b/app/presentation/views/nav.slim index d67baee..c87f09d 100644 --- a/app/presentation/views/nav.slim +++ b/app/presentation/views/nav.slim @@ -1,7 +1,7 @@ nav role="navigation" class="navbar navbar-expand-sm navbar-dark bg-primary" div class="container-fluid" a class="navbar-brand" rel="home" href="/" title="Securely Share Configurations" - img alt="Credence" src="/logo.png" style="max-width:35px; margin-top: -7px;" + img alt="Credence" src="/logo.png" class="logo" a class="navbar-brand" href="/" Credence button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavbar" aria-controls="mainNavbar" aria-expanded="false" aria-label="Toggle navigation" span class="navbar-toggler-icon" diff --git a/app/presentation/views/project.slim b/app/presentation/views/project.slim index 0be5011..8266390 100644 --- a/app/presentation/views/project.slim +++ b/app/presentation/views/project.slim @@ -28,7 +28,7 @@ div class="row" div class="col-xs-4" - if project.policies.can_add_documents button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newDocumentModal" + New Document - div class="row" style="height:10px;" + div class="row mt-2" div class="col-lg-3" div class="panel panel-default" div class="panel-heading" Collaborators diff --git a/app/presentation/views/projects_all.slim b/app/presentation/views/projects_all.slim index 77d2286..749f22a 100644 --- a/app/presentation/views/projects_all.slim +++ b/app/presentation/views/projects_all.slim @@ -1,7 +1,7 @@ -div class="row" style="vertical-align: bottom;" - div class="col-xs-9" +div class="row mt-2" + div class="col-md-9" h3 Projects - div class="col-xs-3" + div class="col-md-3" button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newProjectModal" + New Project table class="table" thead diff --git a/app/services/authenticate_account.rb b/app/services/authenticate_account.rb index 6ce6735..9720c21 100644 --- a/app/services/authenticate_account.rb +++ b/app/services/authenticate_account.rb @@ -10,18 +10,18 @@ class NotAuthenticatedError < StandardError; end class ApiServerError < StandardError; end def call(username:, password:) + credentials = { username: username, password: password } + response = HTTP.post("#{ENV['API_URL']}/auth/authenticate", - json: { username:, password: }) + json: SignedMessage.sign(credentials)) raise(NotAuthenticatedError) if response.code == 401 raise(ApiServerError) if response.code != 200 account_info = JSON.parse(response.to_s)['data']['attributes'] - { - account: account_info['account'], - auth_token: account_info['auth_token'] - } + { account: account_info['account'], + auth_token: account_info['auth_token'] } end end end diff --git a/app/services/authorize_github_account.rb b/app/services/authorize_github_account.rb index 22da3d8..4cee169 100644 --- a/app/services/authorize_github_account.rb +++ b/app/services/authorize_github_account.rb @@ -36,17 +36,19 @@ def get_access_token_from_github(code) end def get_sso_account_from_api(access_token) - response = - HTTP.post("#{@config.API_URL}/auth/sso", - json: { access_token: access_token }) + signed_sso_info = { access_token: access_token } + .then { |sso_info| SignedMessage.sign(sso_info) } + + response = HTTP.post( + "#{@config.API_URL}/auth/sso", + json: signed_sso_info + ) raise if response.code >= 400 account_info = JSON.parse(response)['data']['attributes'] - { - account: account_info['account'], - auth_token: account_info['auth_token'] - } + { account: account_info['account'], + auth_token: account_info['auth_token'] } end end end diff --git a/app/services/create_account.rb b/app/services/create_account.rb index c6757fd..c69345d 100644 --- a/app/services/create_account.rb +++ b/app/services/create_account.rb @@ -15,13 +15,11 @@ def initialize(config) end def call(email:, username:, password:) - message = { email:, - username:, - password: } + account = { email: , username: , password: } response = HTTP.post( "#{@config.API_URL}/accounts/", - json: message + json: SignedMessage.sign(account) ) raise InvalidAccount unless response.code == 201 diff --git a/app/services/verify_registration.rb b/app/services/verify_registration.rb index a1a2e61..d66cf74 100644 --- a/app/services/verify_registration.rb +++ b/app/services/verify_registration.rb @@ -17,8 +17,11 @@ def call(registration_data) registration_data['verification_url'] = "#{@config.APP_URL}/auth/register/#{registration_token}" - response = HTTP.post("#{@config.API_URL}/auth/register", - json: registration_data) + response = HTTP.post( + "#{@config.API_URL}/auth/register", + json: SignedMessage.sign(registration_data) + ) + raise(VerificationError) unless response.code == 202 JSON.parse(response.to_s) diff --git a/config/environments.rb b/config/environments.rb index eb5be5b..2edbff4 100644 --- a/config/environments.rb +++ b/config/environments.rb @@ -4,7 +4,6 @@ require 'roda' require 'figaro' require 'logger' -require 'rack/ssl-enforcer' require 'rack/session/redis' require_relative '../require_app' @@ -31,6 +30,7 @@ def self.logger = LOGGER configure do SecureMessage.setup(ENV.delete('MSG_KEY')) + SignedMessage.setup(config) end configure :production do @@ -40,6 +40,8 @@ def self.logger = LOGGER use Rack::Session::Redis, expire_after: ONE_MONTH, + httponly: true, + same_site: :strict, redis_server: { url: ENV.delete('REDIS_TLS_URL'), ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } @@ -60,6 +62,8 @@ def self.logger = LOGGER # use Rack::Session::Redis, # expire_after: ONE_MONTH, + # httponly: true, + # same_site: :strict, # redis_server: { # url: ENV.delete('REDIS_URL') # } diff --git a/config/secrets.example.yml b/config/secrets.example.yml index 732ea36..5594ada 100644 --- a/config/secrets.example.yml +++ b/config/secrets.example.yml @@ -7,6 +7,8 @@ development: SESSION_SECRET: ND3mZpWrmpAEEjke2N7eJn65IrUPLtCxwYSaql1b0L0+LgaxMPik2Eb9DEYsNSV4SWC4FCOdKtsQ8IIangRYDQ== MSG_KEY: Hb9DLQWSXyisHm8CArBXaIw9crv/wpKTVObm6u8xhxw= REDIS_URL: + SIGNING_KEY: nNuYJVHnMTPfArqFo3Rb81xvXoPDuqcdoUhjWVfGooE= + REDIS_TLS_URL: GH_OAUTH_URL: https://github.com/login/oauth/authorize GH_TOKEN_URL: https://github.com/login/oauth/access_token GH_SCOPE: user:email @@ -19,6 +21,8 @@ test: SESSION_SECRET: ND3mZpWrmpAEEjke2N7eJn65IrUPLtCxwYSaql1b0L0+LgaxMPik2Eb9DEYsNSV4SWC4FCOdKtsQ8IIangRYDQ== MSG_KEY: Hb9DLQWSXyisHm8CArBXaIw9crv/wpKTVObm6u8xhxw= REDIS_URL: + SIGNING_KEY: nNuYJVHnMTPfArqFo3Rb81xvXoPDuqcdoUhjWVfGooE= + REDIS_TLS_URL: GH_OAUTH_URL: https://github.com/login/oauth/authorize GH_TOKEN_URL: https://github.com/login/oauth/access_token GH_SCOPE: user:email @@ -30,6 +34,7 @@ production: APP_URL: http://localhost:9292 SESSION_SECRET: MSG_KEY: + SIGNING_KEY: REDIS_TLS_URL: GH_OAUTH_URL: https://github.com/login/oauth/authorize GH_TOKEN_URL: https://github.com/login/oauth/access_token diff --git a/spec/integration/service_authenticate_spec.rb b/spec/integration/service_authenticate_spec.rb index a26e209..08a6f1c 100644 --- a/spec/integration/service_authenticate_spec.rb +++ b/spec/integration/service_authenticate_spec.rb @@ -27,7 +27,7 @@ auth_return_json = File.read(auth_account_file) WebMock.stub_request(:post, "#{API_URL}/auth/authenticate") - .with(body: @credentials.to_json) + .with(body: SignedMessage.sign(@credentials).to_json) .to_return(body: auth_return_json, headers: { 'content-type' => 'application/json' }) @@ -41,7 +41,7 @@ it 'BAD: should not find a false authenticated account' do WebMock.stub_request(:post, "#{API_URL}/auth/authenticate") - .with(body: @mal_credentials.to_json) + .with(body: SignedMessage.sign(@mal_credentials).to_json) .to_return(status: 401) _(proc { Credence::AuthenticateAccount.new.call(**@mal_credentials) From 5db7231c0315ecc5a8504296b90e40194d41c741 Mon Sep 17 00:00:00 2001 From: Soumya Ray Date: Thu, 28 Apr 2022 15:21:21 +0800 Subject: [PATCH 2/2] Configure browser-side CSPs and security headers --- .rubocop.yml | 8 ++-- Gemfile | 2 +- Gemfile.lock | 6 ++- Rakefile | 5 +-- app/controllers/auth.rb | 3 +- app/controllers/security.rb | 69 ++++++++++++++++++++++++++++++ app/presentation/views/layout.slim | 2 +- config/environments.rb | 30 ++++++------- 8 files changed, 96 insertions(+), 29 deletions(-) create mode 100644 app/controllers/security.rb diff --git a/.rubocop.yml b/.rubocop.yml index 1691893..88ef3ae 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -20,10 +20,10 @@ Security/YAMLLoad: Exclude: - spec/**/* -# Style/HashSyntax: -# Enabled: true -# Exclude: -# - Rakefile +Style/HashSyntax: + Enabled: true + Exclude: + - Rakefile Style/SymbolArray: Enabled: true diff --git a/Gemfile b/Gemfile index 56fb0e5..99fd241 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' ruby File.read('.ruby-version').strip # Web -gem 'puma', '~> 5' +gem 'puma', '~> 5.3.1' gem 'roda' gem 'slim' diff --git a/Gemfile.lock b/Gemfile.lock index 58dfa27..c7652ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -75,7 +75,7 @@ GEM coderay (~> 1.1) method_source (~> 1.0) public_suffix (4.0.7) - puma (5.6.4) + puma (5.3.2) nio4r (~> 2.0) rack (2.2.3) rack-ssl-enforcer (0.2.9) @@ -115,6 +115,7 @@ GEM rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) ruby-progressbar (1.11.0) + secure_headers (6.3.3) slim (4.1.0) temple (>= 0.7.6, < 0.9) tilt (>= 2.0.6, < 2.1) @@ -141,7 +142,7 @@ DEPENDENCIES minitest minitest-rg pry - puma (~> 5) + puma (~> 5.3.1) rack-ssl-enforcer rack-test rake @@ -152,6 +153,7 @@ DEPENDENCIES roda rubocop rubocop-performance + secure_headers slim webmock diff --git a/Rakefile b/Rakefile index 04cf77b..4e57625 100644 --- a/Rakefile +++ b/Rakefile @@ -1,11 +1,10 @@ # frozen_string_literal: true -# rubocop:disable Style/HashSyntax require 'rake/testtask' require './require_app' task :print_env do - puts "Environment: #{ENV.fetch('RACK_ENV', 'development')}" + puts "Environment: #{ENV['RACK_ENV'] || 'development'}" end desc 'Run application console (pry)' @@ -41,7 +40,6 @@ end namespace :run do # Run in development mode - desc 'Run Web App in development mode' task :dev do sh 'rackup -p 9292' end @@ -82,4 +80,3 @@ namespace :session do puts "#{wiped.count} sessions deleted" end end -# rubocop:enable Style/HashSyntax diff --git a/app/controllers/auth.rb b/app/controllers/auth.rb index 6dfb3ee..935ba04 100644 --- a/app/controllers/auth.rb +++ b/app/controllers/auth.rb @@ -33,8 +33,7 @@ def gh_oauth_url(config) routing.redirect @login_route end - authenticated = AuthenticateAccount.new(App.config) - .call(**credentials.values) + authenticated = AuthenticateAccount.new.call(**credentials.values) current_account = Account.new( authenticated[:account], diff --git a/app/controllers/security.rb b/app/controllers/security.rb new file mode 100644 index 0000000..8f1b0a0 --- /dev/null +++ b/app/controllers/security.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require_relative './app' +require 'roda' +require 'rack/ssl-enforcer' +require 'secure_headers' + +module Credence + # Configuration for the API + class App < Roda + plugin :environments + plugin :multi_route + + FONT_SRC = %w[https://cdn.jsdelivr.net].freeze + SCRIPT_SRC = %w[https://cdn.jsdelivr.net].freeze + STYLE_SRC = %w[https://bootswatch.com https://cdn.jsdelivr.net].freeze + + configure :production do + use Rack::SslEnforcer, hsts: true + end + + ## Uncomment to drop the login session in case of any violation + # use Rack::Protection, reaction: :drop_session + use SecureHeaders::Middleware + + SecureHeaders::Configuration.default do |config| + config.cookies = { + secure: true, + httponly: true, + samesite: { + strict: true + } + } + + config.x_frame_options = 'DENY' + config.x_content_type_options = 'nosniff' + config.x_xss_protection = '1' + config.x_permitted_cross_domain_policies = 'none' + config.referrer_policy = 'origin-when-cross-origin' + + # note: single-quotes needed around 'self' and 'none' in CSPs + # rubocop:disable Lint/PercentStringArray + config.csp = { + report_only: false, + preserve_schemes: true, + default_src: %w['self'], + child_src: %w['self'], + connect_src: %w[wws:], + img_src: %w['self'], + font_src: %w['self'] + FONT_SRC, + script_src: %w['self'] + SCRIPT_SRC, + style_src: %W['self'] + STYLE_SRC, + form_action: %w['self'], + frame_ancestors: %w['none'], + object_src: %w['none'], + block_all_mixed_content: true, + report_uri: %w[/security/report_csp_violation] + } + # rubocop:enable Lint/PercentStringArray + end + + route('security') do |routing| + # POST security/report_csp_violation + routing.post 'report_csp_violation' do + App.logger.warn "CSP VIOLATION: #{request.body.read}" + end + end + end +end diff --git a/app/presentation/views/layout.slim b/app/presentation/views/layout.slim index 57f6c2a..613cbff 100644 --- a/app/presentation/views/layout.slim +++ b/app/presentation/views/layout.slim @@ -6,7 +6,7 @@ html / Themed Bootstrap CSS (Cerulean Theme) - see bootswatch.com for more themes / - default Bootstrap CSS: https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css link rel="shortcut icon" href="#" - link rel="stylesheet" href="https://bootswatch.com/5/cerulean/bootstrap.min.css" integrity="sha384-QIHD79FAZ7/24rhhTveEfwJuiAeoPTOQpcVTWsfuPqh3JhPaV6LW8H0cTshmT0Jn" crossorigin="anonymous" + link rel="stylesheet" href="https://bootswatch.com/5/cerulean/bootstrap.min.css" integrity="sha384-6PpWmaLt7GOhVYBzVUBXgwhaV2S6iHthYodyuX2lCQlqAmt6RHXOh+wbLF6hunfO" crossorigin="anonymous" link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/FortAwesome/Font-Awesome@5.15.3/css/all.min.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous" / Custom CSS diff --git a/config/environments.rb b/config/environments.rb index 2edbff4..417c049 100644 --- a/config/environments.rb +++ b/config/environments.rb @@ -16,28 +16,25 @@ class App < Roda # Environment variables setup Figaro.application = Figaro::Application.new( - environment:, + environment: environment, path: File.expand_path('config/secrets.yml') ) Figaro.load - def self.config = Figaro.env + def self.config() = Figaro.env # Logger setup LOGGER = Logger.new($stderr) - def self.logger = LOGGER + def self.logger() = LOGGER ONE_MONTH = 30 * 24 * 60 * 60 configure do + SecureSession.setup(ENV['REDIS_TLS_URL']) # REDIS_TLS_URL used again below SecureMessage.setup(ENV.delete('MSG_KEY')) SignedMessage.setup(config) end configure :production do - SecureSession.setup(ENV.fetch('REDIS_TLS_URL')) # REDIS_TLS_URL used again below - - use Rack::SslEnforcer, hsts: true - use Rack::Session::Redis, expire_after: ONE_MONTH, httponly: true, @@ -49,16 +46,17 @@ def self.logger = LOGGER end configure :development, :test do - require 'pry' - - # NOTE: env var REDIS_URL only used to wipe the session store (ok to be nil) - SecureSession.setup(ENV.fetch('REDIS_URL', nil)) # REDIS_URL used again below - # use Rack::Session::Cookie, - # expire_after: ONE_MONTH, secret: config.SESSION_SECRET + # expire_after: ONE_MONTH, + # secret: config.SESSION_SECRET, + # httponly: true, + # same_site: :strict + use Rack::Session::Pool, - expire_after: ONE_MONTH + expire_after: ONE_MONTH, + httponly: true, + same_site: :strict # use Rack::Session::Redis, # expire_after: ONE_MONTH, @@ -73,7 +71,9 @@ def self.logger = LOGGER require 'pry' # Allows running reload! in pry to restart entire app - def self.reload! = exec 'pry -r ./spec/test_load_all' + def self.reload! + exec 'pry -r ./spec/test_load_all' + end end end end