site-wide password to prevent access to public visitors (WIP)
did
committed Dec 12, 2015
commit f11814344d325e0f71d460a3bb23da704f7a75bd
Showing 5
changed files with
169 additions
and 2 deletions
locomotive/steam/entities/site.rb b/lib/locomotive/steam/entities/site.rb
+3
-1
| @@ | @@ -13,7 +13,9 @@ module Locomotive::Steam |
| template_version: nil, | |
| domains: [], | |
| redirect_to_first_domain: false, | |
| - | url_redirections: [] |
| + | url_redirections: [], |
| + | private_access: false, |
| + | password: nil |
| }.merge(attributes)) | |
| end | |
locomotive/steam/middlewares/private_access.rb b/lib/locomotive/steam/middlewares/private_access.rb
+70
-0
| @@ | @@ -0,0 +1,70 @@ |
| + | module Locomotive::Steam |
| + | module Middlewares |
| + | |
| + | # Hide a site behind a password to prevent public access. |
| + | # If page with the "lock_screen" handle exists, then it |
| + | # will be used to display the login form. Otherwise, a very basic |
| + | # form will be displayed. |
| + | # |
| + | class PrivateAccess < ThreadSafe |
| + | |
| + | include Helpers |
| + | |
| + | def _call |
| + | return if env['steam.private_access_disabled'] |
| + | |
| + | if site.private_access |
| + | log "Site with private access" |
| + | |
| + | if access_granted? |
| + | store_password |
| + | else |
| + | render_response(lock_screen_html, 403) |
| + | end |
| + | end |
| + | end |
| + | |
| + | private |
| + | |
| + | def access_granted? |
| + | !submitted_password.blank? && submitted_password == site.password |
| + | end |
| + | |
| + | def submitted_password |
| + | request.session[:private_access_password] || params[:private_access_password] |
| + | end |
| + | |
| + | def store_password |
| + | request.session[:private_access_password] = params[:private_access_password] if params[:private_access_password].present? |
| + | end |
| + | |
| + | def lock_screen_html |
| + | <<-HTML |
| + | <html> |
| + | <title>#{site.name} - Password protected</title> |
| + | <style> |
| + | @import url(http://fonts.googleapis.com/css?family=Open+Sans:400,700); |
| + | body { background: #f8f8f8; font-family: "Open Sans", sans-serif; font-size: 12px; } |
| + | form { position: relative; top: 50%; width: 300px; margin: 0px auto; transform: translateY(-50%); -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); } |
| + | form p { text-align: center; color: #d9684c; } |
| + | form input[type=password] { border: 2px solid #eee; font-size: 14px; padding: 5px 8px; background: #fff; } |
| + | form input[type=submit] { border: 0 none; padding: 6px 20px; background: #171717; color: #fff; font-size: 14px; text-transform: none; transition: all 100ms ease-in-out; cursor: pointer; } |
| + | form input[type=submit]:hover { opacity: .7; } |
| + | } |
| + | </style> |
| + | <body> |
| + | <form action="/#{mounted_on}" method="POST"> |
| + | #{'<p>Wrong password</p>' unless submitted_password.blank?} |
| + | <input type="password" name="private_access_password" placeholder="Password" /> |
| + | |
| + | <input type="submit" value="Unlock" /> |
| + | </form> |
| + | </body> |
| + | </html> |
| + | HTML |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
locomotive/steam/middlewares/renderer.rb b/lib/locomotive/steam/middlewares/renderer.rb
+2
-1
| @@ | @@ -95,7 +95,8 @@ module Locomotive::Steam |
| 'url' => request.url, | |
| 'ip_address' => request.ip, | |
| 'post?' => request.post?, | |
| - | 'host' => request.host_with_port |
| + | 'host' => request.host_with_port, |
| + | 'mounted_on' => mounted_on |
| } | |
| end | |
locomotive/steam/server.rb b/lib/locomotive/steam/server.rb
+1
-0
| @@ | @@ -59,6 +59,7 @@ module Locomotive::Steam |
| Middlewares::UrlRedirection, | |
| Middlewares::Robots, | |
| Middlewares::Timezone, | |
| + | Middlewares::PrivateAccess, |
| Middlewares::EntrySubmission, | |
| Middlewares::Locale, | |
| Middlewares::LocaleRedirection, | |
spec/unit/middlewares/private_access_spec.rb
+93
-0
| @@ | @@ -0,0 +1,93 @@ |
| + | require 'spec_helper' |
| + | |
| + | require_relative '../../../lib/locomotive/steam/middlewares/thread_safe' |
| + | require_relative '../../../lib/locomotive/steam/middlewares/helpers' |
| + | require_relative '../../../lib/locomotive/steam/middlewares/private_access' |
| + | |
| + | describe Locomotive::Steam::Middlewares::PrivateAccess do |
| + | |
| + | let(:password) { nil } |
| + | let(:site) { instance_double('Site', name: 'Acme Corp', private_access: private_access, password: password) } |
| + | let(:url) { 'http://models.example.com' } |
| + | let(:locomotive_path) { nil } |
| + | let(:session) { {} } |
| + | let(:app) { ->(env) { [200, env, ['app']] } } |
| + | let(:middleware) { described_class.new(app) } |
| + | let(:rack_env) { build_env } |
| + | let(:form) { nil } |
| + | |
| + | subject { code, env, body = middleware.call(rack_env); body.first } |
| + | |
| + | describe 'no private access enabled' do |
| + | |
| + | let(:private_access) { false } |
| + | |
| + | it { is_expected.to eq 'app' } |
| + | |
| + | end |
| + | |
| + | describe 'private access enabled' do |
| + | |
| + | let(:private_access) { true } |
| + | |
| + | context 'no password defined' do |
| + | |
| + | it { is_expected.not_to eq 'app' } |
| + | |
| + | end |
| + | |
| + | context 'password defined' do |
| + | |
| + | let(:password) { 'easyone' } |
| + | let(:form) { 'private_access_password=easyone' } |
| + | |
| + | describe 'right password submitted' do |
| + | |
| + | it { is_expected.to eq 'app' } |
| + | it { subject; expect(session[:private_access_password]).to eq 'easyone' } |
| + | |
| + | end |
| + | |
| + | describe 'right password already stored in the session' do |
| + | |
| + | let(:form) { '' } |
| + | let(:session) { { private_access_password: 'easyone' } } |
| + | |
| + | it { is_expected.to eq 'app' } |
| + | it { subject; expect(session[:private_access_password]).to eq 'easyone' } |
| + | |
| + | end |
| + | |
| + | describe 'wrong password submitted' do |
| + | |
| + | let(:password) { 'easyone' } |
| + | let(:form) { 'private_access_password=wrongone' } |
| + | |
| + | it { is_expected.to match /Wrong password/ } |
| + | |
| + | end |
| + | |
| + | describe 'feature disabled by a specific rack env variable' do |
| + | |
| + | let(:form) { '' } |
| + | |
| + | before { rack_env['steam.private_access_disabled'] = true } |
| + | |
| + | it { is_expected.to eq 'app' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | def build_env |
| + | env_for(url, params: form).tap do |env| |
| + | env['steam.site'] = site |
| + | env['steam.request'] = Rack::Request.new(env) |
| + | env['locomotive.path'] = locomotive_path |
| + | env['rack.session'] = session |
| + | end |
| + | end |
| + | |
| + | end |