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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e7ddb8b903b8df6ad77d9ed41f30c7da735f5f3
|
4
|
+
data.tar.gz: 1a093ad524eb1fbf32977438bca0d4334ead8691
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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"
|
@@ -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: :
|
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
|
-
|
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
|
-
|
21
|
+
@session = current_client.session
|
22
|
+
render json: @session, serializer: Sso::SessionSerializer
|
16
23
|
end
|
17
24
|
|
18
|
-
#
|
25
|
+
# Passport exchange
|
26
|
+
# Passport Strategy first exchange
|
27
|
+
# Insider : Client information from Apps should always be trusted
|
19
28
|
def create
|
20
|
-
|
21
|
-
#
|
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
|
-
|
28
|
-
|
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
|
data/app/models/sso/session.rb
CHANGED
@@ -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.
|
24
|
+
active.find_by!(access_grant_id: grant_id)
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
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
|
37
|
-
|
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
|
46
|
-
|
47
|
-
|
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
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
66
|
-
|
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
|
-
|
52
|
+
attributes = ActionController::Parameters.new(options).permit(:ip, :agent, :location)
|
53
|
+
relations = { application: access_token.application, access_token: access_token }
|
71
54
|
|
72
|
-
|
73
|
-
|
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
|
-
|
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
|
data/db/migrate/{20150414102248_create_sso_sessions.rb → 20150521102248_create_sso_sessions.rb}
RENAMED
@@ -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
|
data/lib/doorkeeper_sso.rb
CHANGED
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
|
|