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