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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -5
  3. data/app/controllers/sso/application_controller.rb +6 -0
  4. data/app/controllers/sso/sessions_controller.rb +76 -12
  5. data/app/models/sso/client.rb +28 -0
  6. data/app/models/sso/session.rb +65 -41
  7. data/app/serializers/sso/owner_serializer.rb +5 -0
  8. data/app/serializers/sso/session_serializer.rb +7 -0
  9. data/db/migrate/{20150414102248_create_sso_sessions.rb → 20150521102248_create_sso_sessions.rb} +2 -3
  10. data/db/migrate/20150521142926_create_sso_clients.rb +18 -0
  11. data/db/migrate/20150521165143_remove_extra_columns_from_sso_sessions.rb +9 -0
  12. data/lib/doorkeeper_sso.rb +1 -0
  13. data/lib/sso.rb +4 -0
  14. data/lib/sso/doorkeeper/access_grant_mixin.rb +12 -0
  15. data/lib/sso/doorkeeper/access_token_mixin.rb +12 -0
  16. data/lib/sso/doorkeeper/application_mixin.rb +12 -0
  17. data/lib/sso/doorkeeper/authorizations_controller_mixin.rb +16 -4
  18. data/lib/sso/doorkeeper/tokens_controller_mixin.rb +15 -5
  19. data/lib/sso/engine.rb +27 -1
  20. data/lib/sso/engine.rb.orig +46 -0
  21. data/lib/sso/logging.rb +1 -1
  22. data/lib/sso/version.rb +1 -1
  23. data/lib/sso/warden/hooks/after_authentication.rb +17 -2
  24. data/lib/sso/warden/hooks/before_logout.rb +14 -5
  25. data/lib/sso/warden/hooks/session_check.rb +45 -0
  26. data/spec/api/schemas/session.json +35 -0
  27. data/spec/controllers/sso/sessions_controller_spec.rb +49 -9
  28. data/spec/fabricators/api_application_fabricator.rb +2 -2
  29. data/spec/fabricators/sso_client_fabricator.rb +5 -0
  30. data/spec/fabricators/sso_session_fabricator.rb +1 -2
  31. data/spec/fabricators/user_fabricator.rb +5 -3
  32. data/spec/lib/doorkeeper/access_grant_mixin_spec.rb +29 -0
  33. data/spec/lib/doorkeeper/access_token_mixin_spec.rb +29 -0
  34. data/spec/lib/doorkeeper/application_mixin_spec.rb +29 -0
  35. data/spec/lib/sso/warden/hooks/after_authentication_spec.rb +37 -0
  36. data/spec/lib/sso/warden/hooks/before_logout_spec.rb +30 -0
  37. data/spec/models/sso/client_spec.rb +15 -0
  38. data/spec/models/sso/session_spec.rb +108 -71
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/support/api_schema_matcher.rb +7 -0
  41. data/spec/test_app/Rakefile +5 -0
  42. data/spec/test_app/db/schema.rb +15 -1
  43. 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
- if code_response
17
- warden_session = session["warden.user.user.session"]
18
- debug { "Sso::Session.update_master_with_grant - #{warden_session["sso_session_id"].inspect}, #{code_response.auth.token.inspect}" }
19
- Sso::Session.update_master_with_grant(warden_session["sso_session_id"], code_response.auth.token)
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
- protected
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
- if sso_session = Sso::Session.update_master_with_access_token(grant_token, outgoing_access_token)
20
- debug { "::Sso::Session.register_access_token success for access_token: #{outgoing_access_token}" }
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
- debug { "::Sso::Session.register_access_token failed. #{sso_session.errors.inspect}" }
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
@@ -5,7 +5,7 @@ module Sso
5
5
  module Logging
6
6
  extend ActiveSupport::Concern
7
7
 
8
- class_methods do
8
+ module ClassMethods
9
9
  def debug(&block)
10
10
  logger && logger.debug(progname, &block)
11
11
  end
data/lib/sso/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sso
2
- VERSION = "0.1.0-alpha"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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
- request = warden.request
22
- session = warden.session(:user)
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 warden.authenticated?(:user) && (session = warden.session(:user))
25
- debug { 'Destroy all Sso::Session groups before logout' }
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
- describe "GET show" do
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 :show, format: :json
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(:attributes) { { ip: "10.1.1.1", agent: "Safari" } }
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(:user)).to eq user }
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
 
@@ -1,6 +1,6 @@
1
1
  Fabricator(:api_application) do
2
- name { Faker::Internet.domain_word }
3
- api_key { Faker::Lorem.characters(16) }
2
+ name { FFaker::Internet.domain_word }
3
+ api_key { FFaker::Lorem.characters(16) }
4
4
  end
5
5
 
6
6
  # == Schema Information