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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb363d678a52d56ddf6c80dc8c1cff69deadd606
4
- data.tar.gz: 1d6ac0652e3f375e675f62517f2f3a8b7fbd19f8
3
+ metadata.gz: 3e7ddb8b903b8df6ad77d9ed41f30c7da735f5f3
4
+ data.tar.gz: 1a093ad524eb1fbf32977438bca0d4334ead8691
5
5
  SHA512:
6
- metadata.gz: 4cddcc1c8862063eb257c05822e7bc0976e03eb7ea9ca69c2fea503994090b9258d53b5fe047072f23b73611fc5e53e3d1403761ba911a2558d19cdb845a20ec
7
- data.tar.gz: 7b20aa56f12c66ef5dad229e625f85ea94d7a3f78e41061037e8def66057d1d0d01b416415d4eebb701860ebf5177f1d38797b1a17117a522c72825447635c7b
6
+ metadata.gz: aadcaafcef1f23d0dd2e38a77bceb930ef156011a393b691734802830aee6e4be40594cc3e5eaed3eaedc2a710134f0b6f977f05fed0b4c180086c61c53760a6
7
+ data.tar.gz: ae93f59d0693635b805bae280a46008c3e6d66d78468ac2ac646d175a2d71574eab25ce44733485ff41edadeaab9299f3a734ac2b35e8ff627e88312a72c513b
data/Rakefile CHANGED
@@ -7,11 +7,6 @@ end
7
7
  Bundler.require :default, :test
8
8
 
9
9
  require 'rake'
10
- task :environment do
11
- Combustion.initialize!
12
- end
13
- Combustion::Application.load_tasks
14
-
15
10
  APP_RAKEFILE = File.expand_path("../spec/test_app/Rakefile", __FILE__)
16
11
  load 'rails/tasks/engine.rake'
17
12
  require "rspec/core/rake_task"
@@ -0,0 +1,6 @@
1
+ module Sso
2
+ class ApplicationController < ::ApplicationController
3
+ # class ApplicationController < RocketPants::Base
4
+ # extend Apipie::DSL::Controller
5
+ end
6
+ end
@@ -1,32 +1,96 @@
1
1
  module Sso
2
- class SessionsController < ApplicationController
2
+ class SessionsController < Sso::ApplicationController
3
3
 
4
- before_action :authenticate_user!, only: :show
5
- before_action :doorkeeper_authorize!, only: :create
6
- before_action :find_user, only: :create
4
+ before_action :authenticate_user!, only: :jsonp
5
+ before_action :doorkeeper_authorize!, only: [:show, :create]
7
6
 
8
7
  # TODO: Security issue?
9
8
  protect_from_forgery with: :null_session
10
9
 
11
10
  respond_to :json
12
11
 
13
- # Returns a 200 if access is granted
12
+ ################################################################################
13
+ # OAuth2 Endpoint
14
+ ################################################################################
15
+
16
+ # Passport verification
17
+ # Session exists (browser/insider) - return passport state
18
+ # Sessionless (iphone/outsider)
19
+ # Returns passport
14
20
  def show
15
- render :nothing => true
21
+ @session = current_client.session
22
+ render json: @session, serializer: Sso::SessionSerializer
16
23
  end
17
24
 
18
- # Generate an SSO:Session
25
+ # Passport exchange
26
+ # Passport Strategy first exchange
27
+ # Insider : Client information from Apps should always be trusted
19
28
  def create
20
- render json: {}
21
- # @session = Sso::Session.generate(@user, doorkeeper_token, params )
29
+ # passport.load_user!
30
+ # passport.create_chip!
31
+ current_client = ::Sso::Client.find_by_access_token(doorkeeper_token.token)
32
+ current_client.update!(client_params)
33
+
34
+ @session = current_client.session
35
+ render json: @session, status: :created, serializer: Sso::SessionSerializer
36
+ end
37
+
38
+ ################################################################################
39
+ # JSONP endpoint based on Devise session
40
+ ################################################################################
41
+ def jsonp
42
+ # TODO : Check inconsistent
43
+ render :nothing => true
22
44
  # respond_with @session, :location => sso.sessions_url
23
45
  end
24
46
 
25
- protected
26
47
 
27
- def find_user
28
- @user = User.find(doorkeeper_token.resource_owner_id)
48
+ ################################################################################
49
+ # Mobile endpoint
50
+ ################################################################################
51
+ def mobile
52
+ # TODO : Check inconsistent
53
+ render :nothing => true
54
+ # respond_with @session, :location => sso.sessions_url
55
+ end
56
+
57
+
58
+
59
+ protected
60
+
61
+ def current_client
62
+ @client = doorkeeper_token.sso_client
63
+ end
64
+
65
+ def client_params
66
+ params.permit(:ip, :agent)
29
67
  end
30
68
 
31
69
  end
32
70
  end
71
+
72
+
73
+ #passport exchange
74
+ # finding = ::SSO::Server::Passports.find_by_access_token_id(access_token.id)
75
+ # if finding.failure?
76
+ # # This should never happen. Every Access Token should be connected to a Passport.
77
+ # return json_error :passport_not_found
78
+ # end
79
+ # passport = finding.object
80
+
81
+ # ::SSO::Server::Passports.update_activity passport_id: passport.id, request: request
82
+
83
+ # debug { "Attaching user and chip to passport #{passport.inspect}" }
84
+ # passport.load_user!
85
+ # passport.create_chip!
86
+
87
+ # payload = { success: true, code: :here_is_your_passport, passport: passport.export }
88
+ # debug { "Created Passport #{passport.id}, sending it including user #{passport.user.inspect}}" }
89
+
90
+ # [200, { 'Content-Type' => 'application/json' }, [payload.to_json]]
91
+
92
+ #passport verification
93
+
94
+ # if request.get? && request.path == passports_path
95
+ # debug { 'Detected incoming Passport verification request.' }
96
+ # env['warden'].authenticate! :passport
@@ -0,0 +1,28 @@
1
+ module Sso
2
+ class Client < ActiveRecord::Base
3
+ include ::Sso::Logging
4
+
5
+ belongs_to :session, class_name: 'Sso::Session', foreign_key: :sso_session_id
6
+ belongs_to :application, class_name: 'Doorkeeper::Application', inverse_of: :sso_clients
7
+ belongs_to :access_grant, class_name: 'Doorkeeper::AccessGrant', inverse_of: :sso_client
8
+ belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', inverse_of: :sso_client
9
+
10
+ validates :access_grant_id, uniqueness: { allow_nil: true }
11
+ validates :access_token_id, uniqueness: { allow_nil: true }
12
+
13
+ class << self
14
+ def find_by_grant_token(token)
15
+ find_by!(access_grant: ::Doorkeeper::AccessGrant.by_token(token))
16
+ end
17
+
18
+ def find_by_access_token(token)
19
+ find_by!(access_token: ::Doorkeeper::AccessToken.by_token(token))
20
+ end
21
+ end
22
+
23
+ def update_access_token(token)
24
+ oauth_token = ::Doorkeeper::AccessToken.by_token(token)
25
+ update(access_token_id: oauth_token.id, application_id: oauth_token.application.id)
26
+ end
27
+ end
28
+ end
@@ -1,17 +1,15 @@
1
1
  module Sso
2
2
  class Session < ActiveRecord::Base
3
3
  include ::Sso::Logging
4
+
4
5
  # FIXME: Not sure to use application or doorkeeper_application_id
5
6
  belongs_to :application, class_name: 'Doorkeeper::Application' #, inverse_of: :sso_sessions
6
7
  belongs_to :access_grant, class_name: 'Doorkeeper::AccessGrant' #, inverse_of: :sso_sessions
7
8
  belongs_to :access_token, class_name: 'Doorkeeper::AccessToken' #, inverse_of: :sso_sessions
8
9
  belongs_to :owner, class_name: 'User' #, inverse_of: :sso_sessions
10
+ has_many :clients, class_name: 'Sso::Client', foreign_key: :sso_session_id
9
11
 
10
- validates :group_id, presence: true
11
12
  validates :owner_id, presence: true
12
- validates :ip, presence: true
13
- validates :secret, presence: true
14
- validates :access_token_id, uniqueness: { scope: [:owner_id, :revoked_at, :application_id], allow_blank: true }
15
13
  validates :revoke_reason, allow_blank: true, format: { with: /\A[a-z_]+\z/ }
16
14
 
17
15
  scope :active, -> { where(revoked_at: nil) }
@@ -23,59 +21,77 @@ module Sso
23
21
 
24
22
  class << self
25
23
  def master_for(grant_id)
26
- active.master.find_by!(access_grant_id: grant_id)
24
+ active.find_by!(access_grant_id: grant_id)
27
25
  end
28
26
 
29
- def generate_master(user, options)
30
- relations = { owner: user }
31
- attributes = ActionController::Parameters.new(options).permit(:ip, :agent, :location)
32
- debug { "Sso::Session::generate_master for #{user.inspect} - #{attributes.inspect}" }
33
- create!(relations.merge(attributes))
27
+ def with_token_id(token_id)
28
+ includes(:clients).where("sso_clients.access_token_id": token_id)
34
29
  end
35
30
 
36
- def generate(user, access_token, options = {})
37
- master_sso_session = active.master.find_by!(owner_id: user.id, access_token_id: access_token.id)
38
- attributes = ActionController::Parameters.new(options).permit(:ip, :agent, :location)
39
- relations = { owner: user, application: access_token.application, access_token: access_token, group_id: master_sso_session.group_id }
40
-
41
- debug { "Sso::Session::generate for #{user.inspect} - #{access_token.inspect} - #{attributes.inspect}" }
42
- create!(relations.merge(attributes))
31
+ def with_grant_id(grant_id)
32
+ includes(:clients).where("sso_clients.access_grant_id": grant_id)
43
33
  end
44
34
 
45
- def logout(sso_session_id)
46
- sso_session = find(sso_session_id)
47
- group_id = sso_session.group_id
48
-
49
- debug { "Sso::Session#logout - Revoking Session Group #{sso_session.group_id.inspect} from Session #{sso_session.id.inspect}" }
50
- count = where(group_id: group_id).update_all revoked_at: Time.current, revoke_reason: "logout"
51
- debug { "Successfully removed #{count.inspect} sessions." }
52
- count
35
+ def by_access_token(token)
36
+ oauth_token = ::Doorkeeper::AccessToken.by_token(token)
37
+ with_token_id(oauth_token.id)
53
38
  end
54
39
 
55
- def update_master_with_grant(master_sso_session_id, oauth_grant)
56
- master_sso_session = active.master.find(master_sso_session_id)
57
-
58
- if master_sso_session.update_attribute(:access_grant_id, oauth_grant.id)
59
- debug { "#update_master_with_grant : #{master_sso_session.id} with Access Grant ID #{oauth_grant.id} which is #{oauth_grant.token}" }
60
- else
61
- error { "#update_master_with_grant : FAILED to update oauth_grant" }
62
- end
40
+ def generate_master(user, options)
41
+ attributes = ActionController::Parameters.new(options).permit(:ip, :agent, :location)
42
+ sso_session = self.new( owner: user )
43
+ sso_session.clients.build(attributes)
44
+ debug { "Sso::Session::generate_master for #{user.inspect} - #{sso_session.inspect}" }
45
+ sso_session.save!
46
+ sso_session
63
47
  end
64
48
 
65
- def update_master_with_access_token(grant_token, access_token)
66
- oauth_grant = ::Doorkeeper::AccessGrant.by_token(grant_token)
67
- oauth_token = ::Doorkeeper::AccessToken.by_token(access_token)
68
- return false if oauth_token.blank? or oauth_grant.blank?
49
+ def generate(user, access_token, options = {})
50
+ master_sso_session = active.find_by!(owner_id: user.id)
69
51
 
70
- master_sso_session = active.master.find_by!(access_grant_id: oauth_grant.id)
52
+ attributes = ActionController::Parameters.new(options).permit(:ip, :agent, :location)
53
+ relations = { application: access_token.application, access_token: access_token }
71
54
 
72
- if master_sso_session.update_attributes(access_token_id: oauth_token.id, application_id: oauth_token.application_id)
73
- debug { "#register_access_token : #{master_sso_session.id} with Access Token ID #{oauth_token.id} which is #{oauth_token.token}" }
55
+ debug { "Sso::Session::generate for #{user.inspect} - #{access_token.inspect} - #{attributes.inspect}" }
56
+
57
+ if client = master_sso_session_id.clients.find_by(access_token_id: access_token.id)
58
+ client.update_columns(attributes)
74
59
  else
75
- error { "#register_access_token : FAILED to update oauth_access_token_id" }
60
+ master_sso_session.clients.create!(relations.merge(attributes))
76
61
  end
77
62
  master_sso_session
78
63
  end
64
+
65
+ def logout(sso_session_id)
66
+ session = find_by_id(sso_session_id)
67
+ return false if session.blank?
68
+ session.logout
69
+ end
70
+
71
+ # def update_master_with_grant(master_sso_session_id, oauth_grant)
72
+ # master_sso_session = active.find(master_sso_session_id)
73
+
74
+ # if master_sso_session.update_attribute(:access_grant_id, oauth_grant.id)
75
+ # debug { "#update_master_with_grant : #{master_sso_session.id} with Access Grant ID #{oauth_grant.id} which is #{oauth_grant.token}" }
76
+ # else
77
+ # error { "#update_master_with_grant : FAILED to update oauth_grant" }
78
+ # end
79
+ # end
80
+
81
+ # def update_master_with_access_token(grant_token, access_token)
82
+ # oauth_grant = ::Doorkeeper::AccessGrant.by_token(grant_token)
83
+ # oauth_token = ::Doorkeeper::AccessToken.by_token(access_token)
84
+ # return false if oauth_token.blank? or oauth_grant.blank?
85
+
86
+ # master_sso_session = active.with_grant_id(oauth_grant.id).first
87
+
88
+ # if master_sso_session.update_attributes(access_token_id: oauth_token.id, application_id: oauth_token.application_id)
89
+ # debug { "#register_access_token : #{master_sso_session.id} with Access Token ID #{oauth_token.id} which is #{oauth_token.token}" }
90
+ # else
91
+ # error { "#register_access_token : FAILED to update oauth_access_token_id" }
92
+ # end
93
+ # master_sso_session
94
+ # end
79
95
  end
80
96
 
81
97
  def create_session(token, options = {})
@@ -85,6 +101,14 @@ module Sso
85
101
  # ['Sso:Session', owner_id, ip, activity_at].join ', '
86
102
  # end
87
103
 
104
+ def active?
105
+ revoked_at.blank?
106
+ end
107
+
108
+ def logout
109
+ update revoked_at: Time.current, revoke_reason: "logout"
110
+ end
111
+
88
112
  private
89
113
 
90
114
  def ensure_secret
@@ -0,0 +1,5 @@
1
+ module Sso
2
+ class OwnerSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :first_name, :last_name, :email, :lang
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Sso
2
+ class SessionSerializer < ActiveModel::Serializer
3
+ attributes :id, :active?, :secret, :revoked_at, :revoke_reason
4
+
5
+ belongs_to :owner, serializer: Sso::OwnerSerializer
6
+ end
7
+ end
@@ -2,7 +2,7 @@ class CreateSsoSessions < ActiveRecord::Migration
2
2
  def change
3
3
  enable_extension 'uuid-ossp'
4
4
 
5
- create_table :sso_sessions, id: :uuid do |t|
5
+ create_table :sso_sessions, id: :uuid, force: true do |t|
6
6
  t.references "access_grant", index: true
7
7
  t.references "access_token", index: true
8
8
  t.references "application", index: true
@@ -15,7 +15,7 @@ class CreateSsoSessions < ActiveRecord::Migration
15
15
  t.datetime "activity_at", null: false
16
16
  t.datetime "revoked_at"
17
17
  t.string "revoke_reason"
18
- t.timestamps
18
+ t.timestamps null: false
19
19
  end
20
20
 
21
21
  add_index :sso_sessions, [:owner_id, :access_token_id, :application_id], where: 'revoked_at IS NULL AND access_token_id IS NOT NULL', unique: true, name: :one_access_token_per_owner
@@ -24,6 +24,5 @@ class CreateSsoSessions < ActiveRecord::Migration
24
24
  add_index :sso_sessions, :secret
25
25
  add_index :sso_sessions, :ip
26
26
  add_index :sso_sessions, :revoke_reason
27
-
28
27
  end
29
28
  end
@@ -0,0 +1,18 @@
1
+ class CreateSsoClients < ActiveRecord::Migration
2
+ enable_extension 'uuid-ossp'
3
+
4
+ def change
5
+ create_table :sso_clients, id: :uuid do |t|
6
+ t.uuid "sso_session_id", index: true
7
+ t.references "access_grant", index: true
8
+ t.references "access_token", index: true
9
+ t.references "application", index: true
10
+ t.string "ip"
11
+ t.string "agent"
12
+ t.string "location"
13
+ t.string "device"
14
+ t.datetime "activity_at"
15
+ t.timestamps null: false
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ class RemoveExtraColumnsFromSsoSessions < ActiveRecord::Migration
2
+ def change
3
+ change_table(:sso_sessions) do |t|
4
+ t.remove :ip
5
+ t.remove :agent
6
+ t.remove :location
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,4 @@
1
+ require "active_model_serializers"
1
2
  require "sso"
2
3
 
3
4
  module DoorkeeperSso
data/lib/sso.rb CHANGED
@@ -2,6 +2,10 @@ require "sso/engine"
2
2
  require "sso/logging"
3
3
  require "sso/warden/hooks/after_authentication"
4
4
  require "sso/warden/hooks/before_logout"
5
+ require "sso/warden/hooks/session_check"
6
+ require "sso/doorkeeper/access_grant_mixin"
7
+ require "sso/doorkeeper/access_token_mixin"
8
+ require "sso/doorkeeper/application_mixin"
5
9
  require "sso/doorkeeper/authorizations_controller_mixin"
6
10
  require "sso/doorkeeper/tokens_controller_mixin"
7
11
 
@@ -0,0 +1,12 @@
1
+ module Sso
2
+ module Doorkeeper
3
+ module AccessGrantMixin
4
+ extend ActiveSupport::Concern
5
+ include ::Sso::Logging
6
+
7
+ included do
8
+ has_one :sso_client, class_name: 'Sso::Client', foreign_key: :access_grant_id
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Sso
2
+ module Doorkeeper
3
+ module AccessTokenMixin
4
+ extend ActiveSupport::Concern
5
+ include ::Sso::Logging
6
+
7
+ included do
8
+ has_one :sso_client, class_name: 'Sso::Client', foreign_key: :access_token_id
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Sso
2
+ module Doorkeeper
3
+ module ApplicationMixin
4
+ extend ActiveSupport::Concern
5
+ include ::Sso::Logging
6
+
7
+ included do
8
+ has_many :sso_clients, class_name: 'Sso::Client', foreign_key: :application_id
9
+ end
10
+ end
11
+ end
12
+ end