balrog 0.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +56 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile.lock +136 -13
- data/README.md +193 -23
- data/app/assets/stylesheets/balrog/gate.css +15 -4
- data/app/views/balrog/gate.html.erb +17 -26
- data/app/views/layouts/balrog.html.erb +22 -0
- data/balrog.gemspec +1 -0
- data/bin/console +1 -1
- data/lib/balrog.rb +4 -1
- data/lib/balrog/engine.rb +23 -4
- data/lib/balrog/generators.rb +1 -1
- data/lib/balrog/generators/install_generator.rb +3 -2
- data/lib/balrog/generators/view_generator.rb +25 -0
- data/lib/balrog/guard.rb +24 -0
- data/lib/balrog/helpers.rb +6 -2
- data/lib/balrog/middleware.rb +47 -33
- data/lib/balrog/middleware/controller.rb +106 -0
- data/lib/balrog/routes_middleware.rb +30 -0
- data/lib/balrog/version.rb +1 -1
- data/lib/balrog/view_helpers.rb +24 -0
- metadata +28 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c908a9916866b576a2914041573fed0ae64555a1956b3a94a3c36e37476e7cf6
|
|
4
|
+
data.tar.gz: 814d65c1b25cfe3e3a561daed8091643694ce81a70eb543eaa40c09939689183
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58872926e04fd6bafd1bdb12915d5a3e0aad33a7e79a82ac074cad45bd8da011cd1f760aae6cb6a8035fd2f1beb2d4a6f16cc2b2c4e3a1ec78718eef4f61b732
|
|
7
|
+
data.tar.gz: b1c7fd33821672a9f59818fe5a543fca3c5d14221d1fac6ff23b5566ec53e63cc2d8f07323057793094edbb7bf5673c72f64bd802cc97febda0973eb5169e7d3
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
version: 2.1
|
|
2
|
+
|
|
3
|
+
jobs:
|
|
4
|
+
build:
|
|
5
|
+
docker:
|
|
6
|
+
- image: circleci/ruby:2.6.2-node-browsers
|
|
7
|
+
- image: circleci/redis:5.0.4
|
|
8
|
+
|
|
9
|
+
working_directory: ~/repo/spec/dummy-rails-app
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- checkout:
|
|
13
|
+
path: ~/repo
|
|
14
|
+
|
|
15
|
+
# Download and cache dependencies
|
|
16
|
+
- restore_cache:
|
|
17
|
+
name: Restore Rubygems cache
|
|
18
|
+
keys:
|
|
19
|
+
- v1-rubygems-{{ checksum "Gemfile.lock" }}
|
|
20
|
+
# fallback to using the latest cache if no exact match is found
|
|
21
|
+
- v1-rubygems-
|
|
22
|
+
|
|
23
|
+
- run:
|
|
24
|
+
name: Install bundler
|
|
25
|
+
command: |
|
|
26
|
+
gem install bundler:2.0.1
|
|
27
|
+
- run:
|
|
28
|
+
name: Install Ruby dependencies
|
|
29
|
+
command: |
|
|
30
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
|
31
|
+
|
|
32
|
+
- save_cache:
|
|
33
|
+
name: Save Rubygems cache
|
|
34
|
+
paths:
|
|
35
|
+
- ./vendor/bundle
|
|
36
|
+
key: v1-rubygems-{{ checksum "Gemfile.lock" }}
|
|
37
|
+
|
|
38
|
+
# run tests!
|
|
39
|
+
- run:
|
|
40
|
+
name: run tests
|
|
41
|
+
command: |
|
|
42
|
+
mkdir /tmp/test-results
|
|
43
|
+
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
|
|
44
|
+
|
|
45
|
+
bundle exec rspec --format progress \
|
|
46
|
+
--format RspecJunitFormatter \
|
|
47
|
+
--out /tmp/test-results/rspec.xml \
|
|
48
|
+
--format progress \
|
|
49
|
+
$TEST_FILES
|
|
50
|
+
|
|
51
|
+
# collect reports
|
|
52
|
+
- store_test_results:
|
|
53
|
+
path: /tmp/test-results
|
|
54
|
+
- store_artifacts:
|
|
55
|
+
path: /tmp/test-results
|
|
56
|
+
destination: test-results
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 2.0.0
|
|
2
|
+
|
|
3
|
+
- Enhancements
|
|
4
|
+
- added support for single sign-on.
|
|
5
|
+
|
|
6
|
+
- BREAKING
|
|
7
|
+
- Balrog can no longer be initialized via `Rails.application.config.middleware.use Balrog::Middleware`. Instead, you need to configure Balrog with `Balrog::Middleware.setup`. See the [README](https://github.com/pixielabs/balrog#Upgrading-from-1.1-to-2.0) for more info.
|
|
8
|
+
- The instance method `Balrog::Middleware#password_hash` has been converted into a class method `Balrog::Middleware.set_password_hash`. See the [README](https://github.com/pixielabs/balrog#Upgrading-from-1.1-to-2.0) for more info.
|
|
9
|
+
- The instance method `Balrog::Middleware#set_session_expiry` has been converted into a class method `Balrog::Middleware.set_session_expiry`. See the [README](https://github.com/pixielabs/balrog#Upgrading-from-1.1-to-2.0) for more info.
|
|
10
|
+
|
|
11
|
+
# 1.1.0
|
|
12
|
+
|
|
13
|
+
- added `Balrog::Middleware#set_session_expiry`, which would force end users to login again after a certain period of time.
|
|
14
|
+
- added `balrog:view` generator, enabling users to modify their Balrog gate view.
|
|
15
|
+
|
|
16
|
+
# 1.0.0
|
|
17
|
+
|
|
18
|
+
- added `Balrog::RoutesMiddleware` module, which can be used to protect mounted Rack applications.
|
|
19
|
+
- dropped support for Rails < 5.
|
|
20
|
+
|
|
21
|
+
# 0.2.0
|
|
22
|
+
|
|
23
|
+
- added `balrog_logout_button` view helper method.
|
|
24
|
+
|
|
25
|
+
# 0.1.0
|
|
26
|
+
|
|
27
|
+
- initial release.
|
data/Gemfile.lock
CHANGED
|
@@ -1,28 +1,151 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
balrog (0.1
|
|
4
|
+
balrog (2.0.1)
|
|
5
5
|
bcrypt (~> 3.0)
|
|
6
|
+
rails (>= 5)
|
|
6
7
|
|
|
7
8
|
GEM
|
|
8
9
|
remote: https://rubygems.org/
|
|
9
10
|
specs:
|
|
10
|
-
|
|
11
|
+
actioncable (6.0.2.2)
|
|
12
|
+
actionpack (= 6.0.2.2)
|
|
13
|
+
nio4r (~> 2.0)
|
|
14
|
+
websocket-driver (>= 0.6.1)
|
|
15
|
+
actionmailbox (6.0.2.2)
|
|
16
|
+
actionpack (= 6.0.2.2)
|
|
17
|
+
activejob (= 6.0.2.2)
|
|
18
|
+
activerecord (= 6.0.2.2)
|
|
19
|
+
activestorage (= 6.0.2.2)
|
|
20
|
+
activesupport (= 6.0.2.2)
|
|
21
|
+
mail (>= 2.7.1)
|
|
22
|
+
actionmailer (6.0.2.2)
|
|
23
|
+
actionpack (= 6.0.2.2)
|
|
24
|
+
actionview (= 6.0.2.2)
|
|
25
|
+
activejob (= 6.0.2.2)
|
|
26
|
+
mail (~> 2.5, >= 2.5.4)
|
|
27
|
+
rails-dom-testing (~> 2.0)
|
|
28
|
+
actionpack (6.0.2.2)
|
|
29
|
+
actionview (= 6.0.2.2)
|
|
30
|
+
activesupport (= 6.0.2.2)
|
|
31
|
+
rack (~> 2.0, >= 2.0.8)
|
|
32
|
+
rack-test (>= 0.6.3)
|
|
33
|
+
rails-dom-testing (~> 2.0)
|
|
34
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
35
|
+
actiontext (6.0.2.2)
|
|
36
|
+
actionpack (= 6.0.2.2)
|
|
37
|
+
activerecord (= 6.0.2.2)
|
|
38
|
+
activestorage (= 6.0.2.2)
|
|
39
|
+
activesupport (= 6.0.2.2)
|
|
40
|
+
nokogiri (>= 1.8.5)
|
|
41
|
+
actionview (6.0.2.2)
|
|
42
|
+
activesupport (= 6.0.2.2)
|
|
43
|
+
builder (~> 3.1)
|
|
44
|
+
erubi (~> 1.4)
|
|
45
|
+
rails-dom-testing (~> 2.0)
|
|
46
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
47
|
+
activejob (6.0.2.2)
|
|
48
|
+
activesupport (= 6.0.2.2)
|
|
49
|
+
globalid (>= 0.3.6)
|
|
50
|
+
activemodel (6.0.2.2)
|
|
51
|
+
activesupport (= 6.0.2.2)
|
|
52
|
+
activerecord (6.0.2.2)
|
|
53
|
+
activemodel (= 6.0.2.2)
|
|
54
|
+
activesupport (= 6.0.2.2)
|
|
55
|
+
activestorage (6.0.2.2)
|
|
56
|
+
actionpack (= 6.0.2.2)
|
|
57
|
+
activejob (= 6.0.2.2)
|
|
58
|
+
activerecord (= 6.0.2.2)
|
|
59
|
+
marcel (~> 0.3.1)
|
|
60
|
+
activesupport (6.0.2.2)
|
|
61
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
62
|
+
i18n (>= 0.7, < 2)
|
|
63
|
+
minitest (~> 5.1)
|
|
64
|
+
tzinfo (~> 1.1)
|
|
65
|
+
zeitwerk (~> 2.2)
|
|
66
|
+
bcrypt (3.1.13)
|
|
67
|
+
builder (3.2.4)
|
|
68
|
+
concurrent-ruby (1.1.6)
|
|
69
|
+
crass (1.0.6)
|
|
11
70
|
diff-lcs (1.3)
|
|
71
|
+
erubi (1.9.0)
|
|
72
|
+
globalid (0.4.2)
|
|
73
|
+
activesupport (>= 4.2.0)
|
|
74
|
+
i18n (1.8.2)
|
|
75
|
+
concurrent-ruby (~> 1.0)
|
|
76
|
+
loofah (2.5.0)
|
|
77
|
+
crass (~> 1.0.2)
|
|
78
|
+
nokogiri (>= 1.5.9)
|
|
79
|
+
mail (2.7.1)
|
|
80
|
+
mini_mime (>= 0.1.1)
|
|
81
|
+
marcel (0.3.3)
|
|
82
|
+
mimemagic (~> 0.3.2)
|
|
83
|
+
method_source (1.0.0)
|
|
84
|
+
mimemagic (0.3.4)
|
|
85
|
+
mini_mime (1.0.2)
|
|
86
|
+
mini_portile2 (2.4.0)
|
|
87
|
+
minitest (5.14.0)
|
|
88
|
+
nio4r (2.5.2)
|
|
89
|
+
nokogiri (1.10.9)
|
|
90
|
+
mini_portile2 (~> 2.4.0)
|
|
91
|
+
rack (2.2.2)
|
|
92
|
+
rack-test (1.1.0)
|
|
93
|
+
rack (>= 1.0, < 3)
|
|
94
|
+
rails (6.0.2.2)
|
|
95
|
+
actioncable (= 6.0.2.2)
|
|
96
|
+
actionmailbox (= 6.0.2.2)
|
|
97
|
+
actionmailer (= 6.0.2.2)
|
|
98
|
+
actionpack (= 6.0.2.2)
|
|
99
|
+
actiontext (= 6.0.2.2)
|
|
100
|
+
actionview (= 6.0.2.2)
|
|
101
|
+
activejob (= 6.0.2.2)
|
|
102
|
+
activemodel (= 6.0.2.2)
|
|
103
|
+
activerecord (= 6.0.2.2)
|
|
104
|
+
activestorage (= 6.0.2.2)
|
|
105
|
+
activesupport (= 6.0.2.2)
|
|
106
|
+
bundler (>= 1.3.0)
|
|
107
|
+
railties (= 6.0.2.2)
|
|
108
|
+
sprockets-rails (>= 2.0.0)
|
|
109
|
+
rails-dom-testing (2.0.3)
|
|
110
|
+
activesupport (>= 4.2.0)
|
|
111
|
+
nokogiri (>= 1.6)
|
|
112
|
+
rails-html-sanitizer (1.3.0)
|
|
113
|
+
loofah (~> 2.3)
|
|
114
|
+
railties (6.0.2.2)
|
|
115
|
+
actionpack (= 6.0.2.2)
|
|
116
|
+
activesupport (= 6.0.2.2)
|
|
117
|
+
method_source
|
|
118
|
+
rake (>= 0.8.7)
|
|
119
|
+
thor (>= 0.20.3, < 2.0)
|
|
12
120
|
rake (10.5.0)
|
|
13
|
-
rspec (3.
|
|
14
|
-
rspec-core (~> 3.
|
|
15
|
-
rspec-expectations (~> 3.
|
|
16
|
-
rspec-mocks (~> 3.
|
|
17
|
-
rspec-core (3.
|
|
18
|
-
rspec-support (~> 3.
|
|
19
|
-
rspec-expectations (3.
|
|
121
|
+
rspec (3.9.0)
|
|
122
|
+
rspec-core (~> 3.9.0)
|
|
123
|
+
rspec-expectations (~> 3.9.0)
|
|
124
|
+
rspec-mocks (~> 3.9.0)
|
|
125
|
+
rspec-core (3.9.0)
|
|
126
|
+
rspec-support (~> 3.9.0)
|
|
127
|
+
rspec-expectations (3.9.0)
|
|
20
128
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
21
|
-
rspec-support (~> 3.
|
|
22
|
-
rspec-mocks (3.
|
|
129
|
+
rspec-support (~> 3.9.0)
|
|
130
|
+
rspec-mocks (3.9.0)
|
|
23
131
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
24
|
-
rspec-support (~> 3.
|
|
25
|
-
rspec-support (3.
|
|
132
|
+
rspec-support (~> 3.9.0)
|
|
133
|
+
rspec-support (3.9.0)
|
|
134
|
+
sprockets (4.0.0)
|
|
135
|
+
concurrent-ruby (~> 1.0)
|
|
136
|
+
rack (> 1, < 3)
|
|
137
|
+
sprockets-rails (3.2.1)
|
|
138
|
+
actionpack (>= 4.0)
|
|
139
|
+
activesupport (>= 4.0)
|
|
140
|
+
sprockets (>= 3.0.0)
|
|
141
|
+
thor (1.0.1)
|
|
142
|
+
thread_safe (0.3.6)
|
|
143
|
+
tzinfo (1.2.7)
|
|
144
|
+
thread_safe (~> 0.1)
|
|
145
|
+
websocket-driver (0.7.1)
|
|
146
|
+
websocket-extensions (>= 0.1.0)
|
|
147
|
+
websocket-extensions (0.1.4)
|
|
148
|
+
zeitwerk (2.3.0)
|
|
26
149
|
|
|
27
150
|
PLATFORMS
|
|
28
151
|
ruby
|
data/README.md
CHANGED
|
@@ -2,22 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Balrog is
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
[](https://badge.fury.io/rb/balrog)
|
|
6
|
+
[](https://circleci.com/gh/pixielabs/balrog)
|
|
7
|
+
|
|
8
|
+
Balrog is a lightweight authorization library for Ruby on Rails >= 5 written by
|
|
9
|
+
[Pixie Labs](https://pixielabs.io) that can protect your routes. Balrog can be
|
|
10
|
+
configured to authorize users using a simple password or single sign-on or both.
|
|
11
|
+
|
|
12
|
+
- If you choose to protect your routes with a password, the password will be
|
|
13
|
+
stored as a password hash, not plain text, and Balrog provides a lightweight
|
|
14
|
+
HTML form that can be styled and used with password managers.
|
|
15
|
+
- If you choose to configure Balrog to use SSO, you can whitelist multiple email
|
|
16
|
+
domains, allowing groups of users access parts of your app, without circulating
|
|
17
|
+
a password.
|
|
18
|
+
- Balrog's authentication can and should be configured to expire, requiring
|
|
19
|
+
users to sign-in again in accordance with [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#session-expiration) best practices.
|
|
20
|
+
- Balrog can also be used to restrict access to [mounted Rack applications](#Restricting-access-to-mounted-Rack-applications-within-config/routes.rb) like Sidekiq.
|
|
21
|
+
|
|
22
|
+
## Table of Contents
|
|
23
|
+
|
|
24
|
+
- [Installation](#Installation)
|
|
25
|
+
- [Regenerating a password hash](#Regenerating-a-password-hash)
|
|
26
|
+
- [Restricting access in a controller](#Restricting-access-in-a-controller)
|
|
27
|
+
- [Restricting access to mounted Rack applications](#Restricting-access-to-mounted-Rack-applications-within-config/routes.rb)
|
|
28
|
+
- [Logout button](#Logout-button)
|
|
29
|
+
- [Changing session expiry length](#Changing-session-expiry-length)
|
|
30
|
+
- [Configuring the Balrog gate view](#Configuring-the-Balrog-gate-view)
|
|
31
|
+
- [Single Sign On](#Single-Sign-On)
|
|
32
|
+
- [Upgrading from 1.1 to 2.0](#Upgrading-from-1.1-to-2.0)
|
|
33
|
+
- [Contributing](#Contributing)
|
|
21
34
|
|
|
22
35
|
## Installation
|
|
23
36
|
|
|
@@ -29,10 +42,10 @@ gem 'balrog'
|
|
|
29
42
|
|
|
30
43
|
Run the installer to generate an initializer:
|
|
31
44
|
|
|
32
|
-
```
|
|
45
|
+
```shell
|
|
33
46
|
$ bundle exec rails generate balrog:install
|
|
34
|
-
Enter New Password:
|
|
35
|
-
Confirm New Password:
|
|
47
|
+
Enter New Password:
|
|
48
|
+
Confirm New Password:
|
|
36
49
|
create config/initializers/balrog.rb
|
|
37
50
|
$
|
|
38
51
|
```
|
|
@@ -60,8 +73,169 @@ class AdminController < ApplicationController
|
|
|
60
73
|
end
|
|
61
74
|
```
|
|
62
75
|
|
|
76
|
+
## Restricting access to mounted Rack applications within config/routes.rb
|
|
77
|
+
|
|
78
|
+
Use the `.use` [method](https://www.rubydoc.info/gems/rack/Rack%2FBuilder:use) to add Balrog to the 'stack'.
|
|
79
|
+
|
|
80
|
+
For example with Sidekiq::Web...
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# Then we tell SideKiq to use Balrog::RoutesMiddleware
|
|
84
|
+
Sidekiq::Web.use Balrog::RoutesMiddleware
|
|
85
|
+
|
|
86
|
+
mount Sidekiq::Web => '/sidekiq'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
N.B. If you are mounting Sidekiq Web, you need to [disable Sidekiq Web's session in config/initializers/sidekiq.rb](https://github.com/mperham/sidekiq/issues/3377#issuecomment-381254940).
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
require 'sidekiq/web'
|
|
93
|
+
|
|
94
|
+
# In order to force sidekiq to use the rails app's session,
|
|
95
|
+
# we need to disable the Sidekiq's session.
|
|
96
|
+
Sidekiq::Web.disable(:sessions)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Logout button
|
|
100
|
+
|
|
101
|
+
To add a logout button, you can call the `balrog_logout_button` view helper
|
|
102
|
+
method and pass in a hash of HTML options to style it. After logout, the user
|
|
103
|
+
will be redirected to the root of the app.
|
|
104
|
+
|
|
105
|
+
For example, in your view:
|
|
106
|
+
|
|
107
|
+
```erb
|
|
108
|
+
<ul class='nav'>
|
|
109
|
+
<li>....</li>
|
|
110
|
+
<li><%= balrog_logout_button 'Admin Logout' %></li>
|
|
111
|
+
<li>....</li>
|
|
112
|
+
</ul>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Other usage examples:
|
|
116
|
+
|
|
117
|
+
```erb
|
|
118
|
+
<%= balrog_logout_button %>
|
|
119
|
+
<%= balrog_logout_button "Leave this place" %>
|
|
120
|
+
<%= balrog_logout_button "Click me", class: 'fancy-button--with-custom-text' %>
|
|
121
|
+
<%= balrog_logout_button class: 'fancy-button--with-default-text' %>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Changing session expiry length
|
|
125
|
+
|
|
126
|
+
`set_session_expiry` requires the user to login again after a period of time.
|
|
127
|
+
To customise this value, open `config/initializers/balrog.rb` after running `balrog:install`
|
|
128
|
+
and change the argument being passed to `set_session_expiry`.
|
|
129
|
+
|
|
130
|
+
The argument passed to `set_session_expiry` can be any of the
|
|
131
|
+
[Rails time extensions](https://api.rubyonrails.org/classes/Numeric.html).
|
|
132
|
+
|
|
133
|
+
If you don't want sessions to expire, remove `set_session_expiry`
|
|
134
|
+
from the initializer completely.
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
Balrog::Middleware.setup do |config|
|
|
138
|
+
config.password_hash '$2a$12$BLz7XCFdG9YfwL64KlTgY.T3FY55aQk8SZEzHfpHfw15F2uN1kuSi'
|
|
139
|
+
config.set_session_expiry 30.minutes
|
|
140
|
+
end
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuring the Balrog gate view
|
|
144
|
+
|
|
145
|
+
We built Balrog to have a default view and stylesheet so that you can drop
|
|
146
|
+
Balrog into your project and everything should “just work”.
|
|
147
|
+
However, we don't want to be in your way if you needed to customise
|
|
148
|
+
your Balrog gate view.
|
|
149
|
+
|
|
150
|
+
If you want to customise the Balrog view, you can run the `balrog:view`
|
|
151
|
+
generator, which will copy the required view and layout to your application:
|
|
152
|
+
|
|
153
|
+
```shell
|
|
154
|
+
$ rails generate balrog:view
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
After running the generator, you can now add elements and classes to the
|
|
158
|
+
`views/balrog/gate.html.erb`, add styles to the
|
|
159
|
+
`assets/stylesheets/application.css` and import the application stylesheet in
|
|
160
|
+
`app/views/layouts/balrog.html.erb`. For an example, see the
|
|
161
|
+
[dummy-rails-app](https://github.com/pixielabs/balrog/tree/master/spec/dummy-rails-app) in the spec folder.
|
|
162
|
+
|
|
163
|
+
## Single Sign On
|
|
164
|
+
|
|
165
|
+
To add single sign on you will need to add the [omniauth gem](https://github.com/omniauth/omniauth)
|
|
166
|
+
to your gem file, along with the omniauth gem for your chosen
|
|
167
|
+
[provider](https://github.com/omniauth/omniauth/wiki/List-of-Strategies).
|
|
168
|
+
|
|
169
|
+
In `config/initializers/balrog.rb`, call `config.set_omniauth` in the setup block.
|
|
170
|
+
`.set_omniauth` takes the same arguments as the `OmniAuth::Builder#provider`
|
|
171
|
+
[method](https://github.com/omniauth/omniauth#getting-started),
|
|
172
|
+
a provider and any required keys.
|
|
173
|
+
|
|
174
|
+
To whitelist any email addresses with a specific domain, call
|
|
175
|
+
`config.set_domain_whitelist`in the setup block and pass in the domain.
|
|
176
|
+
If you want to whitelist multiple domains, you can pass multiple domains
|
|
177
|
+
to the `.set_domain_whitelist`.
|
|
178
|
+
|
|
179
|
+
Balrog does not require a password to be set if you wish to use single sign-on only.
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
Balrog::Middleware.setup do |config|
|
|
183
|
+
credentials = Rails.application.credentials
|
|
184
|
+
config.set_omniauth :google_oauth2, credentials.google_client_id, credentials.google_client_secret
|
|
185
|
+
config.set_domain_whitelist 'pixielabs.io', 'the_fellowship.com'
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
**Please note:** there is currently a CSRF vulnerability which affects OmniAuth
|
|
189
|
+
(designated [CVE-2015-9284](https://nvd.nist.gov/vuln/detail/CVE-2015-9284))
|
|
190
|
+
that requires mitigation at the application level. More details on how to do
|
|
191
|
+
this can be found on the [Omniauth Wiki](https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284).
|
|
192
|
+
|
|
193
|
+
## Upgrading from 1.1 to 2.0
|
|
194
|
+
|
|
195
|
+
To upgrade, you will need to change your Balrog initializer.
|
|
196
|
+
|
|
197
|
+
1. Instead of calling `Rails.application.config.middleware.use Balrog::Middleware`, you will now need to call `Balrog::Middleware.setup`.
|
|
198
|
+
|
|
199
|
+
2. Change the block you pass into these methods. `#password_hash` and `#set_session_expiry` now need to called on a block parameter, e.g `set_session_expiry 30.minutes` needs to be changed to `config.set_session_expiry 30.minutes`.
|
|
200
|
+
|
|
201
|
+
See below for code examples.
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
# Balrog 1.1
|
|
205
|
+
Rails.application.config.middleware.use Balrog::Middleware do
|
|
206
|
+
password_hash '$2a$12$I8Fp3e2GfSdM7KFyoMx56.BVdHeeyk9DQWKkdsxw7USvU/mC8a8.q'
|
|
207
|
+
set_session_expiry 30.minutes
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
# Balrog 2.0
|
|
213
|
+
Balrog::Middleware.setup do |config|
|
|
214
|
+
config.set_password_hash '$2a$12$9lquJW6mVYYS1pD1xYMGzulyC6sEDuLIUfkA/Y7F3RQ8psLNYyLeO'
|
|
215
|
+
config.set_session_expiry 30.minutes
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
63
219
|
## Contributing
|
|
64
220
|
|
|
221
|
+
### Running the tests
|
|
222
|
+
|
|
223
|
+
Tests are part of the dummy Rails app within the spec folder. To run the tests:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
$ cd spec/dummy-rails-app
|
|
227
|
+
$ bundle
|
|
228
|
+
$ rails generate active_record:session_migration
|
|
229
|
+
$ redis-server
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Then in a different terminal:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
$ cd spec/dummy-rails-app
|
|
236
|
+
$ rspec
|
|
237
|
+
```
|
|
238
|
+
|
|
65
239
|
Before contributing, please read the [code of conduct](CODE_OF_CONDUCT.md).
|
|
66
240
|
- Check out the latest master to make sure the feature hasn't been implemented
|
|
67
241
|
or the bug hasn't been fixed yet.
|
|
@@ -74,11 +248,7 @@ Before contributing, please read the [code of conduct](CODE_OF_CONDUCT.md).
|
|
|
74
248
|
want to have your own version, or is otherwise necessary, that is fine, but
|
|
75
249
|
please isolate to its own commit so we can cherry-pick around it.
|
|
76
250
|
|
|
77
|
-
|
|
78
251
|
## TODO
|
|
79
252
|
|
|
80
253
|
* Restricting access via `routes.rb`
|
|
81
|
-
* Logout
|
|
82
254
|
* Test coverage
|
|
83
|
-
* Check it's OK with Ruby on Rails 6
|
|
84
|
-
* Expire sessions
|
|
@@ -27,8 +27,7 @@ footer {
|
|
|
27
27
|
align-self: center;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
-webkit-appearance: button;
|
|
30
|
+
.button_background {
|
|
32
31
|
overflow: visible;
|
|
33
32
|
text-transform: none;
|
|
34
33
|
-webkit-transition-duration: 0.4s;
|
|
@@ -39,13 +38,25 @@ button {
|
|
|
39
38
|
font-size: 20px;
|
|
40
39
|
font-family: 'Helvetica Neue', 'Helvetica', Calibri, 'Trebuchet MS', sans-serif;
|
|
41
40
|
cursor: pointer;
|
|
42
|
-
padding: 8px 10px;
|
|
43
41
|
border: 0;
|
|
44
42
|
font-weight: 100;
|
|
45
43
|
letter-spacing: 1px;
|
|
44
|
+
margin: 5px 0px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
button {
|
|
48
|
+
-webkit-appearance: button;
|
|
49
|
+
padding: 8px 10px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.sso_button {
|
|
53
|
+
display: inline-block;
|
|
54
|
+
text-decoration: none;
|
|
55
|
+
padding: 8px 0px;
|
|
56
|
+
width: 305.438px;
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
button:hover {
|
|
59
|
+
button:hover, .sso_button:hover {
|
|
49
60
|
background-color: rgb(201, 41, 41);
|
|
50
61
|
}
|
|
51
62
|
|
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
</
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
<footer>
|
|
19
|
-
<a href="https://github.com/pixielabs/balrog" target="_blank">
|
|
20
|
-
<%= image_tag "balrog/logo.png", class: 'logo' %>
|
|
21
|
-
</a>
|
|
22
|
-
</footer>
|
|
23
|
-
|
|
24
|
-
</body>
|
|
25
|
-
|
|
26
|
-
</html>
|
|
1
|
+
<section>
|
|
2
|
+
<div>
|
|
3
|
+
<% if show_balrog_password_prompt? %>
|
|
4
|
+
<form action='/balrog/signin' method='POST'>
|
|
5
|
+
<input autofocus type='password' name='password' placeholder='Password'>
|
|
6
|
+
<button type='submit' class="button_background">Login</button>
|
|
7
|
+
</form>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if balrog_omniauth_configured? %>
|
|
10
|
+
<%= button_to(
|
|
11
|
+
"Sign in with SSO",
|
|
12
|
+
"/auth/#{Balrog::Middleware.omniauth_config[:provider]}",
|
|
13
|
+
class: 'button_background sso_button'
|
|
14
|
+
) %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
17
|
+
</section>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang=en>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset=utf-8>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Login</title>
|
|
7
|
+
<%= stylesheet_link_tag "balrog/gate" %>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
|
|
12
|
+
<%= yield %>
|
|
13
|
+
|
|
14
|
+
<footer>
|
|
15
|
+
<a href="https://github.com/pixielabs/balrog" target="_blank">
|
|
16
|
+
<%= image_tag "balrog/logo.png", class: 'logo' %>
|
|
17
|
+
</a>
|
|
18
|
+
</footer>
|
|
19
|
+
|
|
20
|
+
</body>
|
|
21
|
+
|
|
22
|
+
</html>
|
data/balrog.gemspec
CHANGED
data/bin/console
CHANGED
data/lib/balrog.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
require 'rails'
|
|
2
|
+
|
|
2
3
|
module Balrog
|
|
4
|
+
require_relative 'balrog/version'
|
|
3
5
|
require_relative 'balrog/middleware'
|
|
6
|
+
require_relative 'balrog/routes_middleware'
|
|
4
7
|
require_relative 'balrog/engine'
|
|
5
8
|
require_relative 'balrog/rake_tasks'
|
|
6
9
|
require_relative 'balrog/generators'
|
data/lib/balrog/engine.rb
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
require_relative 'helpers'
|
|
2
|
-
|
|
3
1
|
class Balrog::Engine < Rails::Engine
|
|
4
|
-
# Make
|
|
5
|
-
initializer "balrog.
|
|
2
|
+
# Make authenticate_with_balrog! available.
|
|
3
|
+
initializer "balrog.action_controller" do
|
|
6
4
|
ActiveSupport.on_load(:action_controller) do
|
|
5
|
+
require_relative 'helpers'
|
|
7
6
|
include Balrog::Helpers
|
|
8
7
|
end
|
|
9
8
|
end
|
|
10
9
|
|
|
10
|
+
# Add balrog_logout_button as a global view helper.
|
|
11
|
+
initializer "balrog.action_view" do
|
|
12
|
+
ActiveSupport.on_load(:action_view) do
|
|
13
|
+
require_relative 'view_helpers'
|
|
14
|
+
include Balrog::ViewHelpers
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
11
18
|
# Precompile the Balrog assets
|
|
12
19
|
initializer "balrog.assets.precompile" do |app|
|
|
13
20
|
app.config.assets.precompile += %w(
|
|
@@ -15,4 +22,16 @@ class Balrog::Engine < Rails::Engine
|
|
|
15
22
|
balrog/logo.png
|
|
16
23
|
)
|
|
17
24
|
end
|
|
25
|
+
|
|
26
|
+
# Insert Balrog into middleware stack.
|
|
27
|
+
initializer "Balrog.middleware", after: :load_config_initializers, before: :build_middleware_stack do |app|
|
|
28
|
+
# If OmniAuth configured, insert OmniAuth into middleware stack.
|
|
29
|
+
omniauth_config = Balrog::Middleware.omniauth_config
|
|
30
|
+
if omniauth_config
|
|
31
|
+
app.middleware.use OmniAuth::Builder do
|
|
32
|
+
provider omniauth_config[:provider], *omniauth_config[:args]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
app.middleware.use Balrog::Middleware
|
|
36
|
+
end
|
|
18
37
|
end
|
data/lib/balrog/generators.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This Railtie makes the Balrog Generators available from the command line.
|
|
2
2
|
class Balrog::Generators < Rails::Railtie
|
|
3
3
|
generators do
|
|
4
|
-
|
|
4
|
+
Dir[File.join(__dir__, 'generators', '*.rb')].each { |file| require file }
|
|
5
5
|
end
|
|
6
6
|
end
|
|
@@ -6,8 +6,9 @@ class Balrog::InstallGenerator < Rails::Generators::Base
|
|
|
6
6
|
def create_initializer_file
|
|
7
7
|
password_hash = PasswordHasher.encrypt_password
|
|
8
8
|
contents = <<~EOF
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
Balrog::Middleware.setup do |config|
|
|
10
|
+
config.set_password_hash '#{password_hash}'
|
|
11
|
+
config.set_session_expiry 30.minutes
|
|
11
12
|
end
|
|
12
13
|
EOF
|
|
13
14
|
create_file "config/initializers/balrog.rb", contents
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class Balrog::ViewGenerator < Rails::Generators::Base
|
|
2
|
+
|
|
3
|
+
desc "Copies the Balrog gate view and layout into your application, where you can edit and style them."
|
|
4
|
+
def copy_gate_view
|
|
5
|
+
gate_view = File.open(
|
|
6
|
+
File.join(__dir__, '../../../', 'app/views/balrog/gate.html.erb'),
|
|
7
|
+
'r')
|
|
8
|
+
|
|
9
|
+
content = gate_view.read
|
|
10
|
+
gate_view.close
|
|
11
|
+
|
|
12
|
+
create_file "app/views/balrog/gate.html.erb", content
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def copy_layout
|
|
16
|
+
gate_view = File.open(
|
|
17
|
+
File.join(__dir__, '../../../', 'app/views/layouts/balrog.html.erb'),
|
|
18
|
+
'r')
|
|
19
|
+
|
|
20
|
+
content = gate_view.read
|
|
21
|
+
gate_view.close
|
|
22
|
+
|
|
23
|
+
create_file "app/views/layouts/balrog.html.erb", content
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/balrog/guard.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Contains authentication logic to check the user has been authenticated,
|
|
2
|
+
# and that the session hasn't expired.
|
|
3
|
+
module Balrog::Guard
|
|
4
|
+
def authenticated?(balrog_session)
|
|
5
|
+
@balrog_session = balrog_session&.with_indifferent_access
|
|
6
|
+
previously_authenticated? && still_valid?
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# A method to check that the user has been authenticated before.
|
|
12
|
+
def previously_authenticated?
|
|
13
|
+
return false unless @balrog_session
|
|
14
|
+
@balrog_session[:value] == 'authenticated'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# A method to check that the authentication has not expired.
|
|
18
|
+
def still_valid?
|
|
19
|
+
# If the user did not set configured the Balrog session
|
|
20
|
+
# to expire, the cookie is valid.
|
|
21
|
+
return true unless @balrog_session[:expiry_date]
|
|
22
|
+
DateTime.current < @balrog_session[:expiry_date]
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/balrog/helpers.rb
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
require_relative 'guard'
|
|
2
|
+
|
|
1
3
|
# Helpers methods are made available in all controllers by the code in engine.rb.
|
|
2
4
|
module Balrog::Helpers
|
|
5
|
+
include Balrog::Guard
|
|
6
|
+
|
|
3
7
|
def authenticate_with_balrog!
|
|
4
|
-
unless session[:balrog]
|
|
5
|
-
render 'balrog/gate', layout:
|
|
8
|
+
unless authenticated?(session[:balrog])
|
|
9
|
+
render 'balrog/gate', layout: 'balrog'
|
|
6
10
|
end
|
|
7
11
|
end
|
|
8
12
|
end
|
data/lib/balrog/middleware.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'bcrypt'
|
|
2
|
+
require_relative 'middleware/controller'
|
|
2
3
|
|
|
3
4
|
# Public: Balrog middleware that handles form submissions, checking the
|
|
4
5
|
# password against the configured hash, and setting a session variable if
|
|
@@ -7,60 +8,73 @@ require 'bcrypt'
|
|
|
7
8
|
# This is typically set up in an initialize when you run
|
|
8
9
|
# `rails g balrog:install`, and looks a bit like this:
|
|
9
10
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# Balrog::Middleware.setup do |config|
|
|
12
|
+
# config.set_password_hash '<bcrypt hash>'
|
|
13
|
+
# end
|
|
14
|
+
|
|
13
15
|
class Balrog::Middleware
|
|
14
|
-
|
|
16
|
+
include Controller
|
|
17
|
+
|
|
18
|
+
mattr_reader :password_hash
|
|
19
|
+
mattr_reader :session_length
|
|
20
|
+
mattr_reader :omniauth_config
|
|
21
|
+
mattr_reader :domain_whitelist
|
|
22
|
+
|
|
23
|
+
def initialize(app)
|
|
15
24
|
@app = app
|
|
16
|
-
instance_eval(&block) if block_given?
|
|
17
25
|
end
|
|
18
26
|
|
|
19
27
|
def call(env)
|
|
20
28
|
path = env["PATH_INFO"]
|
|
21
29
|
method = env["REQUEST_METHOD"]
|
|
22
|
-
if
|
|
23
|
-
|
|
30
|
+
if login_request?(path, method)
|
|
31
|
+
password_login(env)
|
|
32
|
+
elsif omniauth_request?(path, method)
|
|
33
|
+
omniauthentication(env)
|
|
34
|
+
elsif logout_request?(path, method)
|
|
35
|
+
logout(env)
|
|
24
36
|
else
|
|
25
37
|
@app.call(env)
|
|
26
38
|
end
|
|
27
39
|
end
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def password_hash(input)
|
|
32
|
-
@password_hash = BCrypt::Password.new(input)
|
|
41
|
+
def self.setup
|
|
42
|
+
yield self
|
|
33
43
|
end
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
if env['rack.request.form_hash']
|
|
37
|
-
submitted_password = env['rack.request.form_hash']['password']
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
unless submitted_password
|
|
41
|
-
return [302, {"Location" => referer}, [""]]
|
|
42
|
-
end
|
|
45
|
+
private
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
def self.set_password_hash(input)
|
|
48
|
+
@@password_hash = BCrypt::Password.new(input)
|
|
49
|
+
end
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
def self.set_omniauth(provider, *args)
|
|
52
|
+
@@omniauth_config = {
|
|
53
|
+
provider: provider,
|
|
54
|
+
args: args
|
|
55
|
+
}
|
|
56
|
+
end
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
def self.set_domain_whitelist(*domains)
|
|
59
|
+
@@domain_whitelist = domains
|
|
60
|
+
end
|
|
55
61
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
def self.set_session_expiry(time_period)
|
|
63
|
+
@@session_length = time_period
|
|
64
|
+
end
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
def login_request?(path, method)
|
|
67
|
+
method == 'POST' && path == '/balrog/signin'
|
|
68
|
+
end
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
def omniauth_request?(path, method)
|
|
71
|
+
omniauth_config &&
|
|
72
|
+
method == "GET" &&
|
|
73
|
+
path == "/auth/#{omniauth_config[:provider]}/callback"
|
|
63
74
|
end
|
|
64
75
|
|
|
76
|
+
def logout_request?(path, method)
|
|
77
|
+
method == "DELETE" && path == '/balrog/logout'
|
|
78
|
+
end
|
|
65
79
|
end
|
|
66
80
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Methods that are called in response to specific application requests.
|
|
2
|
+
class Balrog::Middleware
|
|
3
|
+
module Controller
|
|
4
|
+
|
|
5
|
+
# This method is called if a user attempts to sign in with a password
|
|
6
|
+
# and will authenticate the user if the password is correct.
|
|
7
|
+
def password_login(env)
|
|
8
|
+
# Extract the submitted_password from the rack request hash.
|
|
9
|
+
if env['rack.request.form_hash']
|
|
10
|
+
submitted_password = env['rack.request.form_hash']['password']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# If there is no submitted_password, redirect the user before authentication.
|
|
14
|
+
unless submitted_password
|
|
15
|
+
return [302, {"Location" => referer}, [""]]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# If there is no password_hash, alert the developer.
|
|
19
|
+
unless password_hash
|
|
20
|
+
warn <<~EOF
|
|
21
|
+
|
|
22
|
+
!! Balrog has not been configured with a password_hash. You shall not
|
|
23
|
+
!! pass! When adding Balrog::Middleware to your middleware stack, pass
|
|
24
|
+
!! in a block and call `password_hash` passing in a bcrypt hash.
|
|
25
|
+
!!
|
|
26
|
+
!! Check out https://github.com/pixielabs/balrog for more information.
|
|
27
|
+
|
|
28
|
+
EOF
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Authenticate the user if the submitted_password matches the password_hash.
|
|
32
|
+
if password_hash == submitted_password
|
|
33
|
+
authenticate_user(env)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
referer = env["HTTP_REFERER"] || '/'
|
|
37
|
+
|
|
38
|
+
[302, {"Location" => referer}, [""]]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# This method is called if a user attempts to sign in with Single Sign-on
|
|
43
|
+
# and will authenticate the user if email domain has been whitelisted.
|
|
44
|
+
def omniauthentication(env)
|
|
45
|
+
# Extract the email domain from the omniauth hash.
|
|
46
|
+
if env['omniauth.auth']['info']['email']
|
|
47
|
+
user_email = env['omniauth.auth']['info']['email']
|
|
48
|
+
email_domain = user_email.split("@").last
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# If there is no email domain, redirect the user before authentication.
|
|
52
|
+
unless email_domain
|
|
53
|
+
return [302, {"Location" => referer}, [""]]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# If there is no domain_whitelist, alert the developer.
|
|
57
|
+
unless domain_whitelist
|
|
58
|
+
warn <<~EOF
|
|
59
|
+
|
|
60
|
+
!! Balrog has not been configured with a domain_whitelist. You shall not
|
|
61
|
+
!! pass! When setting up Balrog::Middleware, pass in a block and
|
|
62
|
+
!! call `set_domain_whitelist` passing in an omniauth provider and
|
|
63
|
+
!! required keys.
|
|
64
|
+
!!
|
|
65
|
+
!! Check out https://github.com/pixielabs/balrog for more information.
|
|
66
|
+
|
|
67
|
+
EOF
|
|
68
|
+
return [302, {"Location" => referer}, [""]]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Authenticate the user if the user's email domain is whitelisted.
|
|
72
|
+
if domain_whitelist.include?(email_domain)
|
|
73
|
+
authenticate_user(env)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
referer = env["omniauth.origin"] || '/'
|
|
77
|
+
|
|
78
|
+
[302, {"Location" => referer}, [""]]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# This method is called if a user logs out using a balrog logout button.
|
|
83
|
+
# It will achieve this by removing all balrog data from the session.
|
|
84
|
+
def logout(env)
|
|
85
|
+
env['rack.session'].delete(:balrog)
|
|
86
|
+
[302, {"Location" => '/'}, [""]]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# This method marks the user as 'authenicated'.
|
|
92
|
+
def authenticate_user(env)
|
|
93
|
+
session_data = { value: 'authenticated' }
|
|
94
|
+
add_expiry_date!(session_data)
|
|
95
|
+
env['rack.session'][:balrog] = session_data
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# If the user configured the Balrog session to expire, add the
|
|
99
|
+
# expiry_date to the Balrog session.
|
|
100
|
+
def add_expiry_date!(session_data)
|
|
101
|
+
if session_length
|
|
102
|
+
session_data[:expiry_date] = DateTime.current + session_length
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative 'guard'
|
|
2
|
+
|
|
3
|
+
# Public: Balrog routes middleware that redirects the user to a security
|
|
4
|
+
# gate unless the session includes { 'balrog' => 'authenticated' }.
|
|
5
|
+
#
|
|
6
|
+
# In order to protect SideKiq Web you would do something like this:
|
|
7
|
+
#
|
|
8
|
+
# require 'sidekiq/web'
|
|
9
|
+
#
|
|
10
|
+
# Sidekiq::Web.disable(:sessions)
|
|
11
|
+
# Sidekiq::Web.use Balrog::RoutesMiddleware
|
|
12
|
+
#
|
|
13
|
+
# mount Sidekiq::Web => '/sidekiq'
|
|
14
|
+
|
|
15
|
+
class Balrog::RoutesMiddleware
|
|
16
|
+
include Balrog::Guard
|
|
17
|
+
|
|
18
|
+
def initialize(app)
|
|
19
|
+
@app = app
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(env)
|
|
23
|
+
unless authenticated?(env['rack.session']['balrog'])
|
|
24
|
+
html = ApplicationController.renderer.render 'balrog/gate', layout: 'balrog'
|
|
25
|
+
return [200, {"Content-Type" => "text/html"}, [html]]
|
|
26
|
+
end
|
|
27
|
+
@app.call(env)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
data/lib/balrog/version.rb
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ViewHelpers methods are made available in all views by the code in engine.rb.
|
|
2
|
+
module Balrog::ViewHelpers
|
|
3
|
+
def balrog_logout_button(options = nil, html_options = nil)
|
|
4
|
+
name = 'Logout'
|
|
5
|
+
html_options ||= {}
|
|
6
|
+
html_options[:method] = 'delete'
|
|
7
|
+
|
|
8
|
+
if options.is_a?(String)
|
|
9
|
+
name = options
|
|
10
|
+
elsif options.is_a?(Hash)
|
|
11
|
+
html_options = html_options.merge(options)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
button_to(name, '/balrog/logout', html_options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def show_balrog_password_prompt?
|
|
18
|
+
!!Balrog::Middleware.password_hash || !Balrog::Middleware.omniauth_config
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def balrog_omniauth_configured?
|
|
22
|
+
!!Balrog::Middleware.omniauth_config
|
|
23
|
+
end
|
|
24
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: balrog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pixie Labs
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-09-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bcrypt
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '3.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rails
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '5'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '5'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: bundler
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -74,9 +88,11 @@ executables: []
|
|
|
74
88
|
extensions: []
|
|
75
89
|
extra_rdoc_files: []
|
|
76
90
|
files:
|
|
91
|
+
- ".circleci/config.yml"
|
|
77
92
|
- ".gitignore"
|
|
78
93
|
- ".rspec"
|
|
79
94
|
- ".travis.yml"
|
|
95
|
+
- CHANGELOG.md
|
|
80
96
|
- CODE_OF_CONDUCT.md
|
|
81
97
|
- Gemfile
|
|
82
98
|
- Gemfile.lock
|
|
@@ -86,6 +102,7 @@ files:
|
|
|
86
102
|
- app/assets/images/balrog/logo.png
|
|
87
103
|
- app/assets/stylesheets/balrog/gate.css
|
|
88
104
|
- app/views/balrog/gate.html.erb
|
|
105
|
+
- app/views/layouts/balrog.html.erb
|
|
89
106
|
- balrog.gemspec
|
|
90
107
|
- bin/console
|
|
91
108
|
- bin/setup
|
|
@@ -93,17 +110,22 @@ files:
|
|
|
93
110
|
- lib/balrog/engine.rb
|
|
94
111
|
- lib/balrog/generators.rb
|
|
95
112
|
- lib/balrog/generators/install_generator.rb
|
|
113
|
+
- lib/balrog/generators/view_generator.rb
|
|
114
|
+
- lib/balrog/guard.rb
|
|
96
115
|
- lib/balrog/helpers.rb
|
|
97
116
|
- lib/balrog/middleware.rb
|
|
117
|
+
- lib/balrog/middleware/controller.rb
|
|
98
118
|
- lib/balrog/password_hasher.rb
|
|
99
119
|
- lib/balrog/rake_tasks.rb
|
|
120
|
+
- lib/balrog/routes_middleware.rb
|
|
100
121
|
- lib/balrog/tasks/generate_hash.rake
|
|
101
122
|
- lib/balrog/version.rb
|
|
123
|
+
- lib/balrog/view_helpers.rb
|
|
102
124
|
homepage: https://github.com/pixielabs/balrog
|
|
103
125
|
licenses:
|
|
104
126
|
- MIT
|
|
105
127
|
metadata: {}
|
|
106
|
-
post_install_message:
|
|
128
|
+
post_install_message:
|
|
107
129
|
rdoc_options: []
|
|
108
130
|
require_paths:
|
|
109
131
|
- lib
|
|
@@ -118,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
118
140
|
- !ruby/object:Gem::Version
|
|
119
141
|
version: '0'
|
|
120
142
|
requirements: []
|
|
121
|
-
rubygems_version: 3.0.
|
|
122
|
-
signing_key:
|
|
143
|
+
rubygems_version: 3.0.3
|
|
144
|
+
signing_key:
|
|
123
145
|
specification_version: 4
|
|
124
146
|
summary: An alternative to HTTP basic auth
|
|
125
147
|
test_files: []
|