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
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