doorkeeper_sso 0.0.3 → 0.0.4

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +13 -12
  3. data/app/controllers/application_controller.rb +3 -0
  4. data/app/controllers/doorkeeper/authorizations_controller.rb +18 -0
  5. data/app/controllers/doorkeeper/tokens_controller.rb +39 -0
  6. data/app/controllers/sso/sessions_controller.rb +29 -0
  7. data/app/middleware/sso/access_token_marker.rb +103 -0
  8. data/app/middleware/sso/authorization_grant_marker.rb +87 -0
  9. data/app/middleware/sso/passport_verification.rb +25 -0
  10. data/app/models/sso/session.rb +137 -0
  11. data/app/views/layouts/doorkeeper/admin.html.erb +34 -0
  12. data/app/views/layouts/doorkeeper/application.html.erb +23 -0
  13. data/config/routes.rb +1 -0
  14. data/db/migrate/20150414102248_create_sso_sessions.rb +29 -0
  15. data/lib/doorkeeper_sso.rb +4 -1
  16. data/lib/sso/engine.rb +0 -12
  17. data/lib/sso/logging.rb +58 -0
  18. data/lib/sso/version.rb +1 -1
  19. data/lib/sso.rb +4 -0
  20. data/spec/controllers/sso/sessions_controller_spec.rb +65 -0
  21. data/spec/fabricators/api_application_fabricator.rb +16 -0
  22. data/spec/fabricators/doorkeeper_access_grant_fabricator.rb +4 -0
  23. data/spec/fabricators/doorkeeper_access_token_fabricator.rb +5 -0
  24. data/spec/fabricators/doorkeeper_application_fabricator.rb +5 -0
  25. data/spec/fabricators/sso_session_fabricator.rb +6 -0
  26. data/spec/fabricators/user_fabricator.rb +35 -0
  27. data/spec/models/sso/session_spec.rb +183 -0
  28. data/spec/rails_helper.rb +21 -6
  29. data/spec/support/devise.rb +28 -0
  30. data/spec/test_app/Rakefile +0 -4
  31. data/spec/test_app/app/models/user.rb +39 -0
  32. data/spec/test_app/config/database.yml +5 -20
  33. data/spec/test_app/config/initializers/devise.rb +259 -0
  34. data/spec/test_app/config/initializers/doorkeeper.rb +111 -0
  35. data/spec/test_app/config/routes.rb +5 -1
  36. data/spec/test_app/db/schema.rb +78 -0
  37. metadata +86 -67
  38. data/app/controllers/sso/application_controller.rb +0 -4
  39. data/spec/test_app/README.rdoc +0 -28
  40. data/spec/test_app/app/assets/javascripts/application.js +0 -13
  41. data/spec/test_app/app/assets/stylesheets/application.css +0 -15
  42. data/spec/test_app/app/controllers/application_controller.rb +0 -5
  43. data/spec/test_app/app/helpers/application_helper.rb +0 -2
  44. data/spec/test_app/app/views/layouts/application.html.erb +0 -14
  45. data/spec/test_app/bin/bundle +0 -3
  46. data/spec/test_app/bin/rails +0 -4
  47. data/spec/test_app/bin/rake +0 -4
  48. data/spec/test_app/bin/setup +0 -29
  49. data/spec/test_app/config/application.rb +0 -32
  50. data/spec/test_app/config/boot.rb +0 -5
  51. data/spec/test_app/config/environment.rb +0 -5
  52. data/spec/test_app/config/environments/development.rb +0 -41
  53. data/spec/test_app/config/environments/production.rb +0 -79
  54. data/spec/test_app/config/environments/test.rb +0 -42
  55. data/spec/test_app/config/initializers/assets.rb +0 -11
  56. data/spec/test_app/config/initializers/backtrace_silencers.rb +0 -7
  57. data/spec/test_app/config/initializers/cookies_serializer.rb +0 -3
  58. data/spec/test_app/config/initializers/filter_parameter_logging.rb +0 -4
  59. data/spec/test_app/config/initializers/inflections.rb +0 -16
  60. data/spec/test_app/config/initializers/mime_types.rb +0 -4
  61. data/spec/test_app/config/initializers/session_store.rb +0 -3
  62. data/spec/test_app/config/initializers/wrap_parameters.rb +0 -14
  63. data/spec/test_app/config/locales/en.yml +0 -23
  64. data/spec/test_app/config/secrets.yml +0 -22
  65. data/spec/test_app/config.ru +0 -4
  66. data/spec/test_app/public/404.html +0 -67
  67. data/spec/test_app/public/422.html +0 -67
  68. data/spec/test_app/public/500.html +0 -66
data/lib/sso.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  require "sso/engine"
2
+ require "sso/logging"
2
3
 
3
4
  module Sso
5
+ def self.table_name_prefix
6
+ 'sso_'
7
+ end
4
8
  end
@@ -0,0 +1,65 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Sso::SessionsController, :type => :controller do
4
+ routes { Sso::Engine.routes }
5
+ render_views
6
+
7
+ describe "GET show" do
8
+ let(:user) { Fabricate(:user) }
9
+
10
+ context "logged_in" do
11
+ before() { sign_in user }
12
+
13
+ it "returns not authorized" do
14
+ get :show, format: :json
15
+ expect(response).to have_http_status(:ok)
16
+ end
17
+ end
18
+
19
+ context "not logged_in" do
20
+ it "returns not authorized" do
21
+ get :show, format: :json
22
+ expect(response).to have_http_status(:unauthorized)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "POST create" do
28
+ let(:user) { Fabricate(:user) }
29
+ let(:params) { { :ip => "202.188.0.133", :agent => "Chrome", format: :json } }
30
+
31
+ context "not logged_in" do
32
+ it do
33
+ post :create, params
34
+ expect(response).to have_http_status(:unauthorized)
35
+ end
36
+ end
37
+
38
+ context "logged_in" do
39
+ 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) }
42
+ let(:access_token) { Fabricate("Doorkeeper::AccessToken",
43
+ resource_owner_id: user.id) }
44
+ let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
45
+ resource_owner_id: user.id,
46
+ redirect_uri: 'http://localhost:3002/oauth/callback'
47
+ ) }
48
+
49
+ 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
+ allow(controller).to receive(:doorkeeper_authorize!).and_return(true)
55
+ allow(controller).to receive(:doorkeeper_token).and_return(access_token)
56
+
57
+ post :create, params
58
+ end
59
+
60
+ it { expect(response).to have_http_status(:created) }
61
+ it { expect(assigns(:user)).to eq user }
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,16 @@
1
+ Fabricator(:api_application) do
2
+ name { Faker::Internet.domain_word }
3
+ api_key { Faker::Lorem.characters(16) }
4
+ end
5
+
6
+ # == Schema Information
7
+ # Schema version: 20150320075507
8
+ #
9
+ # Table name: api_applications
10
+ #
11
+ # id :integer not null, primary key
12
+ # name :string
13
+ # api_key :string
14
+ # created_at :datetime not null
15
+ # updated_at :datetime not null
16
+ #
@@ -0,0 +1,4 @@
1
+ Fabricator('Doorkeeper::AccessGrant') do
2
+ application { Fabricate("Doorkeeper::Application") }
3
+ expires_in { 2.hours }
4
+ end
@@ -0,0 +1,5 @@
1
+ Fabricator('Doorkeeper::AccessToken') do
2
+ resource_owner_id { Fabricate(:user).id }
3
+ application { Fabricate("Doorkeeper::Application") }
4
+ expires_in { 2.hours }
5
+ end
@@ -0,0 +1,5 @@
1
+ Fabricator('Doorkeeper::Application') do
2
+ name { sequence(:name) { |n| "Application #{n}" } }
3
+ app_uri { 'https://app.com/callback' }
4
+ redirect_uri { 'https://app.com/callback' }
5
+ end
@@ -0,0 +1,6 @@
1
+ Fabricator('Sso::Session') do
2
+ application_id { 0 }
3
+ ip { "127.0.0.1" }
4
+ agent { "Mozilla Firefox" }
5
+ owner { Fabricate(:user) }
6
+ end
@@ -0,0 +1,35 @@
1
+ Fabricator(:user) do
2
+ first_name Faker::Name.name
3
+ email Faker::Internet.email
4
+ password Faker::Internet.password
5
+ password_confirmation { |attrs| "#{attrs[:password]}" }
6
+ end
7
+
8
+ # == Schema Information
9
+ # Schema version: 20150320075507
10
+ #
11
+ # Table name: users
12
+ #
13
+ # id :integer not null, primary key
14
+ # email :string default(""), not null
15
+ # encrypted_password :string default(""), not null
16
+ # reset_password_token :string
17
+ # reset_password_sent_at :datetime
18
+ # remember_created_at :datetime
19
+ # sign_in_count :integer default("0"), not null
20
+ # current_sign_in_at :datetime
21
+ # last_sign_in_at :datetime
22
+ # current_sign_in_ip :inet
23
+ # last_sign_in_ip :inet
24
+ # created_at :datetime
25
+ # updated_at :datetime
26
+ # first_name :string
27
+ # last_name :string
28
+ # lang :string default("EN")
29
+ # phone :string
30
+ #
31
+ # Indexes
32
+ #
33
+ # index_users_on_email (email) UNIQUE
34
+ # index_users_on_reset_password_token (reset_password_token) UNIQUE
35
+ #
@@ -0,0 +1,183 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Sso::Session, :type => :model do
4
+ describe "associations" do
5
+ it { is_expected.to belong_to(:application).class_name('Doorkeeper::Application') }
6
+ it { is_expected.to belong_to(:access_grant).class_name('Doorkeeper::AccessGrant') }
7
+ it { is_expected.to belong_to(:access_token).class_name('Doorkeeper::AccessToken') }
8
+ it { is_expected.to belong_to(:owner).class_name('User') }
9
+ end
10
+
11
+ describe "validations" do
12
+ pending { is_expected.to validate_presence_of(:group_id) }
13
+ pending { is_expected.to validate_presence_of(:secret) }
14
+ pending { is_expected.to validate_uniqueness_of(:access_token_id).scoped_to([:owner_id, :revoked_at, :application_id]) }
15
+ it { is_expected.to allow_value(nil).for(:access_token_id) }
16
+ end
17
+
18
+ describe "scopes" do
19
+ let(:session) { Fabricate('Sso::Session', revoked_at: nil, application_id: nil) }
20
+ it { expect(Sso::Session.active).to eq [session] }
21
+ it { expect(Sso::Session.master).to eq [session] }
22
+ end
23
+
24
+ describe "::master_for" do
25
+ let(:user) { Fabricate(:user) }
26
+ let(:access_token) { Fabricate('Doorkeeper::AccessToken',
27
+ resource_owner_id: user.id) }
28
+ let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
29
+ application_id: nil,
30
+ resource_owner_id: user.id,
31
+ redirect_uri: 'http://localhost:3002/oauth/callback'
32
+ ) }
33
+
34
+ let(:session) { Fabricate('Sso::Session',
35
+ revoked_at: nil,
36
+ application_id: nil,
37
+ access_token_id: access_token.id,
38
+ access_grant_id: access_grant.id,
39
+ owner: user) }
40
+
41
+ it do
42
+ expect(session.revoked_at).to be_nil
43
+ expect(session.application_id).to be_nil
44
+ expect(Sso::Session.master_for(access_grant.id)).to eq session
45
+ end
46
+ end
47
+
48
+ describe "::generate_master" do
49
+ let(:user) { Fabricate(:user) }
50
+ let(:attributes) { { ip: "10.1.1.1", agent: "Safari" } }
51
+
52
+ context "without access token" do
53
+ it "creates a new session" do
54
+ session = Sso::Session.generate_master(user, attributes)
55
+ expect(session).to eq(Sso::Session.first)
56
+ end
57
+ end
58
+
59
+ context "(failure)" do
60
+ it "raises exception" do
61
+ expect { Sso::Session.generate_master(nil) }.to raise_exception
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "::generate" do
67
+ let(:master_sso_session) { Sso::Session.generate_master(user, attributes) }
68
+ let(:user) { Fabricate(:user) }
69
+ let(:attributes) { { ip: "10.1.1.1", agent: "Safari" } }
70
+ let(:access_token) { Fabricate("Doorkeeper::AccessToken", resource_owner_id: user.id) }
71
+ let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
72
+ application_id: nil,
73
+ resource_owner_id: user.id,
74
+ redirect_uri: 'http://localhost:3002/oauth/callback'
75
+ ) }
76
+ let(:session) { Sso::Session.generate(user, access_token, attributes) }
77
+ before do
78
+ # Notice: We assume our warden/doorkeeper is ok and a master with access grant/token is generated
79
+ master_sso_session.access_token_id = access_token.id
80
+ master_sso_session.access_grant_id = access_grant.id
81
+ master_sso_session.save
82
+ end
83
+
84
+ describe "creates a new session" do
85
+ it { expect(session.access_token_id).to eq access_token.id }
86
+ it { expect(session.application_id).to eq access_token.application.id }
87
+ it { expect(session.group_id).to eq master_sso_session.group_id }
88
+ end
89
+ end
90
+
91
+ describe "::logout" do
92
+ let!(:sso_session) { Fabricate('Sso::Session') }
93
+ let!(:user) { sso_session.owner }
94
+
95
+ it "revokes session" do
96
+ Sso::Session.logout(sso_session.id)
97
+ new_session = Sso::Session.find(sso_session.id)
98
+ expect(new_session.revoked_at).not_to be_blank
99
+ expect(new_session.revoke_reason).to eq("logout")
100
+ end
101
+ end
102
+
103
+ describe "::update_master_with_grant" do
104
+ let(:user) { Fabricate(:user) }
105
+ let(:attributes) { { ip: "10.1.1.1", agent: "Safari" } }
106
+ let(:access_token) { Fabricate("Doorkeeper::AccessToken", resource_owner_id: user.id) }
107
+ let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
108
+ application_id: nil,
109
+ resource_owner_id: user.id,
110
+ redirect_uri: 'http://localhost:3002/oauth/callback'
111
+ ) }
112
+ let!(:master_sso_session) { Sso::Session.generate_master(user, attributes) }
113
+
114
+ context "successful" do
115
+ it "updates master_sso_session.access_grant_id" do
116
+ expect{ Sso::Session.update_master_with_grant(master_sso_session.id, access_grant) }.to change{ master_sso_session.reload.access_grant_id }.from(nil).to(access_grant.id)
117
+ end
118
+ end
119
+ end
120
+
121
+ describe "::update_master_with_access_token" do
122
+ let(:user) { Fabricate(:user) }
123
+ let(:attributes) { { ip: "10.1.1.1", agent: "Safari" } }
124
+ let(:access_token) { Fabricate("Doorkeeper::AccessToken", resource_owner_id: user.id) }
125
+ let(:access_grant) { Fabricate('Doorkeeper::AccessGrant',
126
+ application_id: nil,
127
+ resource_owner_id: user.id,
128
+ redirect_uri: 'http://localhost:3002/oauth/callback'
129
+ ) }
130
+ let!(:master) { Sso::Session.generate_master(user, attributes) }
131
+
132
+ before do
133
+ # Notice: We assume our warden/doorkeeper is ok and a master with grant is generated
134
+ master.access_grant_id = access_grant.id
135
+ master.save
136
+ end
137
+
138
+ context "oauth_token not available" do
139
+ it "returns false" do
140
+ expect( Sso::Session.update_master_with_access_token(access_token.token, 123)).to be_falsey
141
+ end
142
+ end
143
+
144
+ it "updates master.access_token_it" do
145
+ expect{ Sso::Session.update_master_with_access_token(access_grant.token, access_token.token) }.to change{ master.reload.access_token_id }.from(nil).to(access_token.id)
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ # == Schema Information
152
+ # Schema version: 20150330031153
153
+ #
154
+ # Table name: sso_sessions
155
+ #
156
+ # id :uuid not null, primary key
157
+ # access_grant_id :integer
158
+ # access_token_id :integer
159
+ # application_id :integer
160
+ # owner_id :integer not null
161
+ # group_id :string not null
162
+ # secret :string not null
163
+ # ip :inet not null
164
+ # agent :string
165
+ # location :string
166
+ # activity_at :datetime not null
167
+ # revoked_at :datetime
168
+ # revoke_reason :string
169
+ # created_at :datetime not null
170
+ # updated_at :datetime not null
171
+ #
172
+ # Indexes
173
+ #
174
+ # index_sso_sessions_on_access_grant_id (access_grant_id)
175
+ # index_sso_sessions_on_access_token_id (access_token_id)
176
+ # index_sso_sessions_on_application_id (application_id)
177
+ # index_sso_sessions_on_group_id (group_id)
178
+ # index_sso_sessions_on_ip (ip)
179
+ # index_sso_sessions_on_owner_id (owner_id)
180
+ # index_sso_sessions_on_revoke_reason (revoke_reason)
181
+ # index_sso_sessions_on_secret (secret)
182
+ # one_access_token_per_owner (owner_id,access_token_id,application_id) UNIQUE
183
+ #
data/spec/rails_helper.rb CHANGED
@@ -3,13 +3,25 @@ ENV["RAILS_ENV"] ||= 'test'
3
3
 
4
4
  # SimpleCov for rails
5
5
  require 'simplecov'
6
- SimpleCov.start 'rails' do
7
- add_filter "/app/admin/"
8
- end
6
+ SimpleCov.start 'rails'
7
+
9
8
  require 'spec_helper'
10
- require File.expand_path("../../config/environment", __FILE__)
9
+ # require File.expand_path("../test_app/config/environment", __FILE__)
10
+
11
+ # Combustion ordering for requires
12
+ require 'combustion'
13
+ # require 'capybara/rspec'
14
+
15
+ Combustion.path = 'spec/test_app'
16
+ Combustion.initialize! :all
17
+
18
+
11
19
  require 'rspec/rails'
20
+ # require 'capybara/rails'
12
21
  require 'shoulda/matchers'
22
+ require 'fabrication'
23
+ require 'vcr'
24
+
13
25
  # Add additional requires below this line. Rails is not loaded until this point!
14
26
 
15
27
  # Requires supporting ruby files with custom matchers and macros, etc, in
@@ -25,7 +37,10 @@ require 'shoulda/matchers'
25
37
  # directory. Alternatively, in the individual `*_spec.rb` files, manually
26
38
  # require only the support files necessary.
27
39
  #
28
- Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
40
+ Dir[Rails.root.join("../support/**/*.rb")].each { |f| require f }
41
+
42
+ # Require fabricators manually
43
+ Dir[Rails.root.join("../fabricators/**/*fabricator.rb")].each { |f| require f }
29
44
 
30
45
  # Checks for pending migrations before tests are run.
31
46
  # If you are not using ActiveRecord, you can remove this line.
@@ -33,7 +48,7 @@ ActiveRecord::Migration.maintain_test_schema!
33
48
 
34
49
  RSpec.configure do |config|
35
50
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
36
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
51
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
37
52
 
38
53
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
39
54
  # examples within a transaction, remove the following line or assign false
@@ -0,0 +1,28 @@
1
+ module ControllerHelpers
2
+ def sign_in(user = Fabricate(:user))
3
+ if user.nil?
4
+ allow(request.env['warden']).to receive(:authenticate!).
5
+ and_throw(:warden, {:scope => :user})
6
+ allow(controller).to receive_messages :current_user => nil
7
+ else
8
+ allow(request.env['warden']).to receive_messages :authenticate! => user
9
+ allow(controller).to receive_messages :current_user => user
10
+ end
11
+ end
12
+ end
13
+
14
+ RSpec.configure do |config|
15
+ config.include Devise::TestHelpers, :type => :controller
16
+ config.include ControllerHelpers, :type => :controller
17
+
18
+ # rspec-rails 3 will no longer automatically infer an example group's spec type
19
+ # from the file location. You can explicitly opt-in to the feature using this
20
+ # config option.
21
+ # To explicitly tag specs without using automatic inference, set the `:type`
22
+ # metadata manually:
23
+ #
24
+ # describe ThingsController, :type => :controller do
25
+ # # Equivalent to being in spec/controllers
26
+ # end
27
+ config.infer_spec_type_from_file_location!
28
+ end
@@ -1,6 +1,2 @@
1
1
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
2
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
-
4
- require File.expand_path('../config/application', __FILE__)
5
-
6
- Rails.application.load_tasks
@@ -0,0 +1,39 @@
1
+ class User < ActiveRecord::Base
2
+
3
+ # Include default devise modules. Others available are:
4
+ # :confirmable, :lockable, :timeoutable and :omniauthable
5
+ devise :database_authenticatable, :registerable,
6
+ :recoverable, :rememberable, :trackable, :validatable
7
+
8
+ validates :email, :first_name, presence: true
9
+
10
+ end
11
+
12
+ # == Schema Information
13
+ # Schema version: 20150320075507
14
+ #
15
+ # Table name: users
16
+ #
17
+ # id :integer not null, primary key
18
+ # email :string default(""), not null
19
+ # encrypted_password :string default(""), not null
20
+ # reset_password_token :string
21
+ # reset_password_sent_at :datetime
22
+ # remember_created_at :datetime
23
+ # sign_in_count :integer default("0"), not null
24
+ # current_sign_in_at :datetime
25
+ # last_sign_in_at :datetime
26
+ # current_sign_in_ip :inet
27
+ # last_sign_in_ip :inet
28
+ # created_at :datetime
29
+ # updated_at :datetime
30
+ # first_name :string
31
+ # last_name :string
32
+ # lang :string default("EN")
33
+ # phone :string
34
+ #
35
+ # Indexes
36
+ #
37
+ # index_users_on_email (email) UNIQUE
38
+ # index_users_on_reset_password_token (reset_password_token) UNIQUE
39
+ #
@@ -1,25 +1,10 @@
1
- # SQLite version 3.x
2
- # gem install sqlite3
3
- #
4
- # Ensure the SQLite 3 gem is defined in your Gemfile
5
- # gem 'sqlite3'
6
- #
7
1
  default: &default
8
- adapter: sqlite3
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ # For details on connection pooling, see rails configuration guide
5
+ # http://guides.rubyonrails.org/configuring.html#database-pooling
9
6
  pool: 5
10
- timeout: 5000
11
7
 
12
- development:
13
- <<: *default
14
- database: db/development.sqlite3
15
-
16
- # Warning: The database defined as "test" will be erased and
17
- # re-generated from your development database when you run "rake".
18
- # Do not set this db to the same as development or production.
19
8
  test:
20
9
  <<: *default
21
- database: db/test.sqlite3
22
-
23
- production:
24
- <<: *default
25
- database: db/production.sqlite3
10
+ database: doorkeeper_sso_test