doorkeeper_sso 0.1.0.pre.alpha → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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