doorkeeper_sso 0.1.0.pre.alpha → 0.2.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 +4 -4
- data/Rakefile +0 -5
- data/app/controllers/sso/application_controller.rb +6 -0
- data/app/controllers/sso/sessions_controller.rb +76 -12
- data/app/models/sso/client.rb +28 -0
- data/app/models/sso/session.rb +65 -41
- data/app/serializers/sso/owner_serializer.rb +5 -0
- data/app/serializers/sso/session_serializer.rb +7 -0
- data/db/migrate/{20150414102248_create_sso_sessions.rb → 20150521102248_create_sso_sessions.rb} +2 -3
- data/db/migrate/20150521142926_create_sso_clients.rb +18 -0
- data/db/migrate/20150521165143_remove_extra_columns_from_sso_sessions.rb +9 -0
- data/lib/doorkeeper_sso.rb +1 -0
- data/lib/sso.rb +4 -0
- data/lib/sso/doorkeeper/access_grant_mixin.rb +12 -0
- data/lib/sso/doorkeeper/access_token_mixin.rb +12 -0
- data/lib/sso/doorkeeper/application_mixin.rb +12 -0
- data/lib/sso/doorkeeper/authorizations_controller_mixin.rb +16 -4
- data/lib/sso/doorkeeper/tokens_controller_mixin.rb +15 -5
- data/lib/sso/engine.rb +27 -1
- data/lib/sso/engine.rb.orig +46 -0
- data/lib/sso/logging.rb +1 -1
- data/lib/sso/version.rb +1 -1
- data/lib/sso/warden/hooks/after_authentication.rb +17 -2
- data/lib/sso/warden/hooks/before_logout.rb +14 -5
- data/lib/sso/warden/hooks/session_check.rb +45 -0
- data/spec/api/schemas/session.json +35 -0
- data/spec/controllers/sso/sessions_controller_spec.rb +49 -9
- data/spec/fabricators/api_application_fabricator.rb +2 -2
- data/spec/fabricators/sso_client_fabricator.rb +5 -0
- data/spec/fabricators/sso_session_fabricator.rb +1 -2
- data/spec/fabricators/user_fabricator.rb +5 -3
- data/spec/lib/doorkeeper/access_grant_mixin_spec.rb +29 -0
- data/spec/lib/doorkeeper/access_token_mixin_spec.rb +29 -0
- data/spec/lib/doorkeeper/application_mixin_spec.rb +29 -0
- data/spec/lib/sso/warden/hooks/after_authentication_spec.rb +37 -0
- data/spec/lib/sso/warden/hooks/before_logout_spec.rb +30 -0
- data/spec/models/sso/client_spec.rb +15 -0
- data/spec/models/sso/session_spec.rb +108 -71
- data/spec/spec_helper.rb +1 -0
- data/spec/support/api_schema_matcher.rb +7 -0
- data/spec/test_app/Rakefile +5 -0
- data/spec/test_app/db/schema.rb +15 -1
- metadata +75 -18
@@ -13,10 +13,22 @@ module Sso
|
|
13
13
|
def after_grant_create
|
14
14
|
debug { "AuthorizationsController#Create : after_action" }
|
15
15
|
code_response = authorization.instance_variable_get("@response")
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
oauth_grant = code_response.try(:auth).try(:token)
|
17
|
+
|
18
|
+
warden_session = session["warden.user.user.session"]
|
19
|
+
session = Sso::Session.find_by!(id: warden_session["sso_session_id"])
|
20
|
+
|
21
|
+
if session.try(:active?)
|
22
|
+
error { "AuthorizationsControllerMixin - Sso::Session Inactive #{session.inspect}"}
|
23
|
+
warden.logout(:user) and return
|
24
|
+
end
|
25
|
+
|
26
|
+
if oauth_grant
|
27
|
+
debug { "Sso::Session.update_master_with_grant - #{session.id.inspect}, #{oauth_grant.inspect}" }
|
28
|
+
session.clients.find_or_create_by!(access_grant_id: oauth_grant.id)
|
29
|
+
else
|
30
|
+
error { "AuthorizationsControllerMixin - Unable to get grant id"}
|
31
|
+
warden.logout(:user) and return
|
20
32
|
end
|
21
33
|
end
|
22
34
|
end
|
@@ -1,11 +1,16 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
1
3
|
module Sso
|
2
4
|
module Doorkeeper
|
3
5
|
module TokensControllerMixin
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include ::Sso::Logging
|
8
|
+
|
4
9
|
included do
|
5
10
|
after_action :after_token_create, only: :create
|
6
11
|
end
|
7
12
|
|
8
|
-
|
13
|
+
protected
|
9
14
|
|
10
15
|
def after_token_create
|
11
16
|
debug { "TokensController#Create : after_action" }
|
@@ -16,10 +21,15 @@ module Sso
|
|
16
21
|
# We cannot rely on session[:sso_session_id] here because the end-user might have cookies disabled.
|
17
22
|
# The only thing we can rely on to identify the user/Passport is the incoming grant token.
|
18
23
|
debug { %(Detected outgoing "Access Token" #{outgoing_access_token.inspect}) }
|
19
|
-
|
20
|
-
|
24
|
+
|
25
|
+
unless client = ::Sso::Client.find_by_grant_token(grant_token)
|
26
|
+
error { "::Sso::Client not found for grant token #{grant_token}" }
|
27
|
+
end
|
28
|
+
|
29
|
+
if client.update_access_token(outgoing_access_token)
|
30
|
+
debug { "::Sso::Client.update_access_token success for access_token: #{outgoing_access_token}" }
|
21
31
|
else
|
22
|
-
|
32
|
+
error { "::Sso::Session.update_access_token failed. #{client.errors.inspect}" }
|
23
33
|
warden.logout
|
24
34
|
end
|
25
35
|
end
|
@@ -38,4 +48,4 @@ module Sso
|
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|
41
|
-
end
|
51
|
+
end
|
data/lib/sso/engine.rb
CHANGED
@@ -2,7 +2,31 @@ module Sso
|
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
isolate_namespace Sso
|
4
4
|
|
5
|
+
# New test framework integration
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec,
|
8
|
+
:fixtures => true,
|
9
|
+
:view_specs => false,
|
10
|
+
:helper_specs => false,
|
11
|
+
:routing_specs => false,
|
12
|
+
:controller_specs => true,
|
13
|
+
:request_specs => false
|
14
|
+
g.fixture_replacement :fabrication
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer :append_migrations do |app|
|
18
|
+
unless app.root.to_s.match root.to_s
|
19
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
20
|
+
app.config.paths["db/migrate"] << expanded_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
5
25
|
config.after_initialize do
|
26
|
+
::Doorkeeper::Application.send(:include, Sso::Doorkeeper::ApplicationMixin)
|
27
|
+
::Doorkeeper::AccessGrant.send(:include, Sso::Doorkeeper::AccessGrantMixin)
|
28
|
+
::Doorkeeper::AccessToken.send(:include, Sso::Doorkeeper::AccessTokenMixin)
|
29
|
+
|
6
30
|
|
7
31
|
::Doorkeeper::TokensController.send(:include, AbstractController::Callbacks)
|
8
32
|
::Doorkeeper::TokensController.send(:include, Sso::Doorkeeper::TokensControllerMixin)
|
@@ -11,9 +35,11 @@ module Sso
|
|
11
35
|
::Warden::Manager.after_authentication(scope: :user, &::Sso::Warden::Hooks::AfterAuthentication.to_proc)
|
12
36
|
::Warden::Manager.before_logout(scope: :user, &::Sso::Warden::Hooks::BeforeLogout.to_proc)
|
13
37
|
|
38
|
+
# TODO : Infinite loop with before_logout
|
39
|
+
# ::Warden::Manager.after_fetch(scope: :user, &::Sso::Warden::Hooks::SessionCheck.to_proc)
|
40
|
+
|
14
41
|
# TODO : Why does it need a passport strategy
|
15
42
|
# Warden::Strategies.add :passport, ::Sso::Server::Warden::Strategies::Passport
|
16
|
-
|
17
43
|
end
|
18
44
|
end
|
19
45
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Sso
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Sso
|
4
|
+
|
5
|
+
<<<<<<< HEAD
|
6
|
+
# New test framework integration
|
7
|
+
config.generators do |g|
|
8
|
+
g.test_framework :rspec,
|
9
|
+
:fixtures => true,
|
10
|
+
:view_specs => false,
|
11
|
+
:helper_specs => false,
|
12
|
+
:routing_specs => false,
|
13
|
+
:controller_specs => true,
|
14
|
+
:request_specs => false
|
15
|
+
g.fixture_replacement :fabrication
|
16
|
+
=======
|
17
|
+
initializer :append_migrations do |app|
|
18
|
+
unless app.root.to_s.match root.to_s
|
19
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
20
|
+
app.config.paths["db/migrate"] << expanded_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
>>>>>>> 4400323a20d61fedd59372c74cf3d32e72a52f09
|
24
|
+
end
|
25
|
+
|
26
|
+
config.after_initialize do
|
27
|
+
::Doorkeeper::Application.send(:include, Sso::Doorkeeper::ApplicationMixin)
|
28
|
+
::Doorkeeper::AccessGrant.send(:include, Sso::Doorkeeper::AccessGrantMixin)
|
29
|
+
::Doorkeeper::AccessToken.send(:include, Sso::Doorkeeper::AccessTokenMixin)
|
30
|
+
|
31
|
+
|
32
|
+
::Doorkeeper::TokensController.send(:include, AbstractController::Callbacks)
|
33
|
+
::Doorkeeper::TokensController.send(:include, Sso::Doorkeeper::TokensControllerMixin)
|
34
|
+
::Doorkeeper::AuthorizationsController.send(:include, Sso::Doorkeeper::AuthorizationsControllerMixin)
|
35
|
+
|
36
|
+
::Warden::Manager.after_authentication(scope: :user, &::Sso::Warden::Hooks::AfterAuthentication.to_proc)
|
37
|
+
::Warden::Manager.before_logout(scope: :user, &::Sso::Warden::Hooks::BeforeLogout.to_proc)
|
38
|
+
|
39
|
+
# TODO : Infinite loop with before_logout
|
40
|
+
# ::Warden::Manager.after_fetch(scope: :user, &::Sso::Warden::Hooks::SessionCheck.to_proc)
|
41
|
+
|
42
|
+
# TODO : Why does it need a passport strategy
|
43
|
+
# Warden::Strategies.add :passport, ::Sso::Server::Warden::Strategies::Passport
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/sso/logging.rb
CHANGED
data/lib/sso/version.rb
CHANGED
@@ -5,6 +5,8 @@ module Sso
|
|
5
5
|
include ::Sso::Logging
|
6
6
|
|
7
7
|
attr_reader :user, :warden, :options
|
8
|
+
delegate :request, to: :warden
|
9
|
+
delegate :params, to: :request
|
8
10
|
|
9
11
|
def self.to_proc
|
10
12
|
proc do |user, warden, options|
|
@@ -18,9 +20,10 @@ module Sso
|
|
18
20
|
|
19
21
|
def call
|
20
22
|
debug { "Starting hook because this is considered the first login of the current session..." }
|
21
|
-
|
22
|
-
|
23
|
+
generate_session
|
24
|
+
end
|
23
25
|
|
26
|
+
def generate_session
|
24
27
|
debug { "Generating a Sso:Session for user #{user.id.inspect} for the session cookie at the Sso server..." }
|
25
28
|
attributes = { ip: request.ip, agent: request.user_agent }
|
26
29
|
|
@@ -28,6 +31,18 @@ module Sso
|
|
28
31
|
debug { "Sso:Session with ID #{sso_session.id} generated successfuly. Persisting it in session..." }
|
29
32
|
session["sso_session_id"] = sso_session.id.to_s
|
30
33
|
end
|
34
|
+
|
35
|
+
def scope
|
36
|
+
scope = options[:scope]
|
37
|
+
end
|
38
|
+
|
39
|
+
def session
|
40
|
+
warden.session(scope)
|
41
|
+
end
|
42
|
+
|
43
|
+
def logged_in?
|
44
|
+
warden.authenticated?(:user) && session
|
45
|
+
end
|
31
46
|
end
|
32
47
|
end
|
33
48
|
end
|
@@ -7,7 +7,6 @@ module Sso
|
|
7
7
|
attr_reader :user, :warden, :options
|
8
8
|
delegate :request, to: :warden
|
9
9
|
delegate :params, to: :request
|
10
|
-
delegate :session, to: :request
|
11
10
|
|
12
11
|
def self.to_proc
|
13
12
|
proc do |user, warden, options|
|
@@ -21,13 +20,23 @@ module Sso
|
|
21
20
|
|
22
21
|
def call
|
23
22
|
# Only run if user is logged in
|
24
|
-
if
|
25
|
-
debug {
|
26
|
-
debug { session.inspect }
|
23
|
+
if logged_in?
|
24
|
+
debug { "Logout Sso::Session - #{session["sso_session_id"]}" }
|
27
25
|
Sso::Session.logout(session["sso_session_id"])
|
28
|
-
#Passports.logout passport_id: params['passport_id'], provider_passport_id: session['sso_session_id']
|
29
26
|
end
|
30
27
|
end
|
28
|
+
|
29
|
+
def scope
|
30
|
+
scope = options[:scope]
|
31
|
+
end
|
32
|
+
|
33
|
+
def session
|
34
|
+
warden.session(scope)
|
35
|
+
end
|
36
|
+
|
37
|
+
def logged_in?
|
38
|
+
warden.authenticated?(:user) && session
|
39
|
+
end
|
31
40
|
end
|
32
41
|
end
|
33
42
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Sso
|
2
|
+
module Warden
|
3
|
+
module Hooks
|
4
|
+
class SessionCheck
|
5
|
+
include ::Sso::Logging
|
6
|
+
|
7
|
+
attr_reader :user, :warden, :options
|
8
|
+
delegate :request, to: :warden
|
9
|
+
delegate :params, to: :request
|
10
|
+
|
11
|
+
def self.to_proc
|
12
|
+
proc do |user, warden, options|
|
13
|
+
new(user: user, warden: warden, options: options).call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(user:, warden:, options:)
|
18
|
+
@user, @warden, @options = user, warden, options
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
debug { "Starting hook after user is fetched into the session" }
|
23
|
+
|
24
|
+
# Infinite loop with BeforeLogout - before logout runs this too
|
25
|
+
unless Sso::Session.find_by(id: session["sso_session_id"]).try(:active?)
|
26
|
+
warden.logout(:user)
|
27
|
+
throw(:warden, :scope => scope, :reason => "Sso::Session not found")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def scope
|
32
|
+
scope = options[:scope]
|
33
|
+
end
|
34
|
+
|
35
|
+
def session
|
36
|
+
warden.session(scope)
|
37
|
+
end
|
38
|
+
|
39
|
+
def logged_in?
|
40
|
+
warden.authenticated?(:user) && session
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
{
|
2
|
+
"type": "object",
|
3
|
+
"required" : [
|
4
|
+
"id",
|
5
|
+
"active?",
|
6
|
+
"secret",
|
7
|
+
"owner"
|
8
|
+
],
|
9
|
+
"properties": {
|
10
|
+
"id" : { "type" : "string" },
|
11
|
+
"active?" : { "type" : "boolean" },
|
12
|
+
"revoked_at" : { "type": ["string", "null"], "format": "date-time" },
|
13
|
+
"revoke_reason" : { "type": ["string", "null"] },
|
14
|
+
"secret" : { "type" : "string" },
|
15
|
+
"owner" : {
|
16
|
+
"type" : "object",
|
17
|
+
"required" : [
|
18
|
+
"id",
|
19
|
+
"name",
|
20
|
+
"email",
|
21
|
+
"first_name",
|
22
|
+
"last_name",
|
23
|
+
"lang"
|
24
|
+
],
|
25
|
+
"properties" : {
|
26
|
+
"id" : { "type" : "integer" },
|
27
|
+
"name" : { "type" : "string" },
|
28
|
+
"email" : { "type" : "string" },
|
29
|
+
"first_name" : { "type" : "string" },
|
30
|
+
"last_name" : { "type" : "string" },
|
31
|
+
"lang" : { "type" : "string" }
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
@@ -4,24 +4,64 @@ RSpec.describe Sso::SessionsController, :type => :controller do
|
|
4
4
|
routes { Sso::Engine.routes }
|
5
5
|
render_views
|
6
6
|
|
7
|
-
|
7
|
+
pending "GET jsonp" do
|
8
8
|
let(:user) { Fabricate(:user) }
|
9
9
|
|
10
10
|
context "logged_in" do
|
11
11
|
before() { sign_in user }
|
12
12
|
|
13
13
|
it "returns not authorized" do
|
14
|
-
get :
|
14
|
+
get :jsonp, format: :json
|
15
15
|
expect(response).to have_http_status(:ok)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
context "not logged_in" do
|
20
20
|
it "returns not authorized" do
|
21
|
+
get :jsonp, format: :json
|
22
|
+
expect(response).to have_http_status(:unauthorized)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "GET show" do
|
28
|
+
let(:user) { Fabricate(:user) }
|
29
|
+
|
30
|
+
context "not logged_in" do
|
31
|
+
it do
|
21
32
|
get :show, format: :json
|
22
33
|
expect(response).to have_http_status(:unauthorized)
|
23
34
|
end
|
24
35
|
end
|
36
|
+
|
37
|
+
context "logged_in" do
|
38
|
+
let(:user) { Fabricate(:user) }
|
39
|
+
let(:application) { Fabricate('Doorkeeper::Application') }
|
40
|
+
let(:access_token) { Fabricate('Doorkeeper::AccessToken',
|
41
|
+
resource_owner_id: user.id) }
|
42
|
+
let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
|
43
|
+
application_id: application.id,
|
44
|
+
resource_owner_id: user.id,
|
45
|
+
redirect_uri: 'http://localhost:3002/oauth/callback'
|
46
|
+
) }
|
47
|
+
|
48
|
+
let(:session) { Fabricate('Sso::Session', owner: user) }
|
49
|
+
let!(:client) { Fabricate('Sso::Client', session: session,
|
50
|
+
application_id: application.id,
|
51
|
+
access_token_id: access_token.id,
|
52
|
+
access_grant_id: access_grant.id) }
|
53
|
+
|
54
|
+
before do
|
55
|
+
allow(controller).to receive(:doorkeeper_authorize!).and_return(true)
|
56
|
+
allow(controller).to receive(:doorkeeper_token).and_return(access_token)
|
57
|
+
|
58
|
+
get :show, format: :json
|
59
|
+
end
|
60
|
+
|
61
|
+
it { expect(response).to have_http_status(:ok) }
|
62
|
+
it { expect(assigns(:session)).to eq session }
|
63
|
+
it { expect(response).to match_response_schema("session") }
|
64
|
+
end
|
25
65
|
end
|
26
66
|
|
27
67
|
describe "POST create" do
|
@@ -37,8 +77,7 @@ RSpec.describe Sso::SessionsController, :type => :controller do
|
|
37
77
|
|
38
78
|
context "logged_in" do
|
39
79
|
let(:user) { Fabricate(:user) }
|
40
|
-
let(:
|
41
|
-
let(:master_sso_session) { Sso::Session.generate_master(user, attributes) }
|
80
|
+
let(:master_sso_session) { Sso::Session.generate_master(user, { ip: "10.1.1.1", agent: "Safari" }) }
|
42
81
|
let(:access_token) { Fabricate("Doorkeeper::AccessToken",
|
43
82
|
resource_owner_id: user.id) }
|
44
83
|
let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
|
@@ -47,18 +86,19 @@ RSpec.describe Sso::SessionsController, :type => :controller do
|
|
47
86
|
) }
|
48
87
|
|
49
88
|
before do
|
50
|
-
master_sso_session.access_token_id = access_token.id
|
51
|
-
master_sso_session.access_grant_id = access_grant.id
|
52
|
-
master_sso_session.save
|
53
|
-
|
54
89
|
allow(controller).to receive(:doorkeeper_authorize!).and_return(true)
|
55
90
|
allow(controller).to receive(:doorkeeper_token).and_return(access_token)
|
56
91
|
|
92
|
+
# Create a client with access grant & access token
|
93
|
+
master_sso_session.clients.find_or_create_by!(access_grant_id: access_grant.id, access_token_id: access_token.id)
|
57
94
|
post :create, params
|
58
95
|
end
|
59
96
|
|
60
97
|
it { expect(response).to have_http_status(:created) }
|
61
|
-
it { expect(assigns(:
|
98
|
+
it { expect(assigns(:session)).to eq master_sso_session }
|
99
|
+
it { expect(response).to match_response_schema("session") }
|
100
|
+
it { expect(master_sso_session.clients).to include ::Sso::Client.find_by(access_token: access_token) }
|
101
|
+
it { expect(master_sso_session.clients.map(&:ip)).to include "202.188.0.133" }
|
62
102
|
end
|
63
103
|
end
|
64
104
|
|