hobby-sso-guard 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9420789b746af7c4123aec30ee4ea25fa07c33ebf08aa362c37c5fad260d8cfb
4
+ data.tar.gz: c30830fe6b47ea022317944de82e2a6a636e649d794ac34757e43b610f8c26c4
5
+ SHA512:
6
+ metadata.gz: f7bb14045ff3c00b36b80ee835b1d78c720ff8aa883ac2697f84014798201bb8952d8651a5c31ee11725425fc5f1d61ac14b9b39d537556b57d7edda9f1d0120
7
+ data.tar.gz: 4979e6a98d70387c08cbd30b85ae18a3ec169af24a0e423da7056a901e4a91388cca15f795afdca41379f8a6327ed615353461a2e20c3865aa5277a3f192828a
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rspec'
4
+ gem 'rspec-power_assert'
5
+ gem 'pry'
6
+ gem 'awesome_print'
7
+ gem 'puma'
8
+ gem 'redis'
9
+ gem 'watir'
10
+
11
+ gemspec
@@ -0,0 +1,63 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hobby-sso-guard (0.0.0)
5
+ hobby
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ awesome_print (1.8.0)
11
+ childprocess (0.9.0)
12
+ ffi (~> 1.0, >= 1.0.11)
13
+ coderay (1.1.2)
14
+ diff-lcs (1.3)
15
+ ffi (1.9.23)
16
+ hobby (0.1.2)
17
+ rack
18
+ method_source (0.9.0)
19
+ power_assert (1.1.1)
20
+ pry (0.11.3)
21
+ coderay (~> 1.1.0)
22
+ method_source (~> 0.9.0)
23
+ puma (3.11.3)
24
+ rack (2.0.4)
25
+ redis (4.0.1)
26
+ rspec (3.7.0)
27
+ rspec-core (~> 3.7.0)
28
+ rspec-expectations (~> 3.7.0)
29
+ rspec-mocks (~> 3.7.0)
30
+ rspec-core (3.7.1)
31
+ rspec-support (~> 3.7.0)
32
+ rspec-expectations (3.7.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.7.0)
35
+ rspec-mocks (3.7.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.7.0)
38
+ rspec-power_assert (1.1.0)
39
+ power_assert (~> 1.1.0)
40
+ rspec (>= 2.14)
41
+ rspec-support (3.7.1)
42
+ rubyzip (1.2.1)
43
+ selenium-webdriver (3.11.0)
44
+ childprocess (~> 0.5)
45
+ rubyzip (~> 1.2)
46
+ watir (6.10.3)
47
+ selenium-webdriver (~> 3.4, >= 3.4.1)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ awesome_print
54
+ hobby-sso-guard!
55
+ pry
56
+ puma
57
+ redis
58
+ rspec
59
+ rspec-power_assert
60
+ watir
61
+
62
+ BUNDLED WITH
63
+ 1.16.1
@@ -0,0 +1,9 @@
1
+ Gem::Specification.new do |g|
2
+ g.name = 'hobby-sso-guard'
3
+ g.files = `git ls-files`.split($/)
4
+ g.version = '0.0.0'
5
+ g.summary = 'A Rack middleware for SSO(single sign-on).'
6
+ g.authors = ['Anatoly Chernow']
7
+
8
+ g.add_dependency 'hobby'
9
+ end
@@ -0,0 +1,53 @@
1
+ require 'securerandom'
2
+
3
+ module Hobby
4
+ module SSO
5
+ class Guard
6
+ # sessions: { session_id => user_id }
7
+ # tickets: { ticket => session_id } should expire quickly(20 seconds or so)
8
+ # tokens: { token => latest_visited_path }
9
+ def initialize app, sessions:, tickets:, tokens:, auth_server:
10
+ @app, @sessions, @tokens = app, sessions, tokens
11
+ @auth_server = auth_server
12
+ @enter_app = Enter.new tickets, tokens
13
+ end
14
+
15
+ def call env
16
+ @env = env
17
+
18
+ if active_session?
19
+ @app.call env
20
+ else
21
+ if env['PATH_INFO'] == '/enter'
22
+ @enter_app.call env
23
+ else
24
+ redirect_to_auth_server_with_token create_guest_token
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+ attr_reader :env
31
+
32
+ def active_session?
33
+ session = env['rack.session']
34
+ @sessions.exists session[:id]
35
+ end
36
+
37
+ def create_guest_token
38
+ token = SecureRandom.urlsafe_base64 64
39
+ env['rack.session'][:guest_token] = token
40
+ @tokens.hset token, 'path', env['PATH_INFO']
41
+ @tokens.hset token, 'query', env['QUERY_STRING']
42
+ token
43
+ end
44
+
45
+ def redirect_to_auth_server_with_token token
46
+ location = "#{@auth_server}?service=#{env['HTTP_HOST']}&token=#{token}"
47
+ [302, { 'Location' => location }, []]
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ require_relative 'guard/enter'
@@ -0,0 +1,41 @@
1
+ require 'hobby'
2
+
3
+ class Hobby::SSO::Guard
4
+ class Enter
5
+ include Hobby
6
+
7
+ def initialize tickets, tokens
8
+ @tickets, @tokens = tickets, tokens
9
+ end
10
+
11
+ get '/enter' do
12
+ if ticket = request.params['ticket']
13
+ if session_id = @tickets.get(ticket)
14
+ session[:id] = session_id
15
+ redirect_to_latest_visited_path_or_root
16
+ else
17
+ response.status = 403
18
+ 'Bad ticket.'
19
+ end
20
+ else
21
+ response.status = 403
22
+ 'No ticket.'
23
+ end
24
+ end
25
+
26
+ def session
27
+ env['rack.session']
28
+ end
29
+
30
+ def redirect_to_latest_visited_path_or_root
31
+ token = session[:guest_token]
32
+ if @tokens.exists token
33
+ path = @tokens.hget token, 'path'
34
+ query = @tokens.hget token, 'query'
35
+ response.redirect "#{path}?#{query}"
36
+ else
37
+ response.redirect '/'
38
+ end
39
+ end
40
+ end
41
+ end
data/links ADDED
@@ -0,0 +1,3 @@
1
+ https://www.owasp.org/index.php/Testing_for_Session_Management
2
+ https://www.owasp.org/index.php/Session_Management_Cheat_Sheet
3
+ https://www.owasp.org/index.php/Authentication_Cheat_Sheet
@@ -0,0 +1,24 @@
1
+ require 'hobby'
2
+ require 'awesome_print'
3
+
4
+ class AuthServer
5
+ include Hobby
6
+
7
+ get do
8
+ if $auth_server_just_returns_HTTP_HOST
9
+ ap env
10
+ env['HTTP_HOST']
11
+ else
12
+ session, ticket = random, random
13
+ $sessions.set session, 'some_user_id'
14
+ $tickets.set ticket, session
15
+
16
+ response.status = 302
17
+ response.redirect "#{TEST_APP_URL}/enter?ticket=#{ticket}"
18
+ end
19
+ end
20
+
21
+ def random
22
+ SecureRandom.urlsafe_base64 64
23
+ end
24
+ end
@@ -0,0 +1,51 @@
1
+ require 'puma'
2
+ require 'redis'
3
+ require 'watir'
4
+
5
+ require 'rspec/power_assert'
6
+ RSpec::PowerAssert.example_assertion_alias :assert
7
+ RSpec::PowerAssert.example_group_assertion_alias :assert
8
+
9
+ require 'fileutils'
10
+ DIR = "/tmp/sso.#{$$}.test"
11
+ FileUtils.mkdir_p DIR
12
+
13
+
14
+ TEST_APP_URL = 'http://127.0.0.1:8080'
15
+ TEST_APP_PORT = 8080
16
+ AUTH_SERVER_URL = 'http://127.0.0.1:8081'
17
+ AUTH_SERVER_PORT = 8081
18
+
19
+ require_relative 'auth_server'
20
+ AUTH_SERVER = AuthServer.new
21
+
22
+ def start_puma port, app, host = '127.0.0.1'
23
+ server = Puma::Server.new app
24
+ server.add_tcp_listener host, port
25
+ server.run
26
+ end
27
+
28
+ RSpec.configure do |config|
29
+ config.before :suite do |example|
30
+ $sessions_pid = spawn "redis-server --unixsocket #{DIR}/sessions.sock --port 0 --dir #{DIR}"
31
+ $tickets_pid = spawn "redis-server --unixsocket #{DIR}/tickets.sock --port 0 --dir #{DIR}"
32
+ $tokens_pid = spawn "redis-server --unixsocket #{DIR}/tokens.sock --port 0 --dir #{DIR}"
33
+
34
+ $sessions = Redis.new path: "#{DIR}/sessions.sock"
35
+ $tickets = Redis.new path: "#{DIR}/tickets.sock"
36
+ $tokens = Redis.new path: "#{DIR}/tokens.sock"
37
+
38
+ $test_app_pid = fork do
39
+ require_relative 'test_app'
40
+ start_puma TEST_APP_PORT, TestApp.new
41
+ sleep
42
+ end
43
+
44
+ start_puma AUTH_SERVER_PORT, AuthServer.new
45
+ end
46
+
47
+ config.after :suite do |example|
48
+ pids = [$sessions_pid, $tickets_pid, $tokens_pid, $test_app_pid]
49
+ pids.each { |pid| `kill #{pid}` }
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'helper'
2
+
3
+ describe do
4
+ let(:browser) { Watir::Browser.new }
5
+
6
+ context 'when there is no active session' do
7
+ it 'redirects to the auth server' do
8
+ $auth_server_just_returns_HTTP_HOST = true
9
+ browser.goto TEST_APP_URL
10
+ assert { browser.text == '127.0.0.1:8081' }
11
+ end
12
+
13
+ after do
14
+ $auth_server_just_returns_HTTP_HOST = false
15
+ end
16
+ end
17
+
18
+ context 'when a user tries to /enter' do
19
+ it 'refuses if there is no ticket' do
20
+ browser.goto "#{TEST_APP_URL}/enter"
21
+ assert { browser.text == 'No ticket.' }
22
+ end
23
+
24
+ it 'refuses if there is a bad ticket' do
25
+ bad_ticket = SecureRandom.urlsafe_base64 64
26
+ browser.goto "#{TEST_APP_URL}/enter?ticket=#{bad_ticket}"
27
+ assert { browser.text == 'Bad ticket.' }
28
+ end
29
+
30
+ context 'when the ticket is valid' do
31
+ it 'sets a session cookie and redirect to an appropriate path' do
32
+ browser.goto "#{TEST_APP_URL}/some_path"
33
+ assert { browser.text == 'some path in test app' }
34
+ end
35
+ end
36
+ end
37
+
38
+ after do
39
+ browser.quit
40
+ $sessions.flushall
41
+ $tickets.flushall
42
+ $tokens.flushall
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require 'hobby'
2
+ require 'hobby/sso/guard'
3
+
4
+ class TestApp
5
+ include Hobby
6
+
7
+ use Rack::Session::Cookie, secret: SecureRandom.hex(64)
8
+ use Hobby::SSO::Guard, auth_server: AUTH_SERVER_URL,
9
+ sessions: $sessions, tickets: $tickets, tokens: $tokens
10
+
11
+ get do
12
+ 'test app root'
13
+ end
14
+
15
+ get '/some_path' do
16
+ 'some path in test app'
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hobby-sso-guard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Anatoly Chernow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hobby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - Gemfile.lock
35
+ - hobby-sso-guard.gemspec
36
+ - lib/hobby/sso/guard.rb
37
+ - lib/hobby/sso/guard/enter.rb
38
+ - links
39
+ - spec/auth_server.rb
40
+ - spec/helper.rb
41
+ - spec/integration_spec.rb
42
+ - spec/test_app.rb
43
+ homepage:
44
+ licenses: []
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.7.6
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: A Rack middleware for SSO(single sign-on).
66
+ test_files: []