doorkeeper_sso 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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