hobby-sso-guard 0.0.0
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 +7 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +63 -0
- data/hobby-sso-guard.gemspec +9 -0
- data/lib/hobby/sso/guard.rb +53 -0
- data/lib/hobby/sso/guard/enter.rb +41 -0
- data/links +3 -0
- data/spec/auth_server.rb +24 -0
- data/spec/helper.rb +51 -0
- data/spec/integration_spec.rb +44 -0
- data/spec/test_app.rb +18 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -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
data/Gemfile.lock
ADDED
@@ -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,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
data/spec/auth_server.rb
ADDED
@@ -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
|
data/spec/helper.rb
ADDED
@@ -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
|
data/spec/test_app.rb
ADDED
@@ -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: []
|