osso 0.0.5.pre.eta → 0.0.5.pre.gamma

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 914a99a42c898e69186b33f4c51069f8c6f4ceddd7c300ce4158f560cc9d7937
4
- data.tar.gz: 75ece89829bdb5a756340e9c9ff63e414ec093f402d725783a8668459ea32665
3
+ metadata.gz: f8349ed4f80afe46d642e3e1e7aaa9b9d0f90ece6e0fd6b8e0db416f74cc54f1
4
+ data.tar.gz: 9aa776d8e26b04570e4123baf232404c3e9059c66bcb1fe39f2ac337c59e7edd
5
5
  SHA512:
6
- metadata.gz: 4117bbe85a6f88e7703eaf22464d2b343ac4517ec5e6d4a473a7f758ee1733a6b1f65504f13105ec9f823ae2e1f21db0129f0e9aab98ac260175decc5710aaec
7
- data.tar.gz: 05ed7bea73e54d8ce07b8d6b494724b0cc2dc9e06cee78ba2977e249a983c3b579fe847a9ae731806b94ad94b765bf1d2d8c8b9e763245acdf6e783d40d8bbcf
6
+ metadata.gz: aa38f696f541aa36893252c26aa1ffea8b6ac33b05b3bb130ecf7b1b71094042a7546984d67857ba8002c47744c47a1c813e7d972f38068be6c50f35a11aa9b7
7
+ data.tar.gz: 515be1591f3d5ce752057483cfa8e7d94d1665a7534fe1112c5c607f7ec2417ff650367826d7eec71d64086d735994c4f1b7ac322756df103adbdb6c4958bec2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osso (0.0.5.pre.eta)
4
+ osso (0.0.5.pre.gamma)
5
5
  activesupport (>= 6.0.3.2)
6
6
  graphql
7
7
  jwt
@@ -18,12 +18,12 @@ PATH
18
18
  GEM
19
19
  remote: https://rubygems.org/
20
20
  specs:
21
- activemodel (6.0.3.3)
22
- activesupport (= 6.0.3.3)
23
- activerecord (6.0.3.3)
24
- activemodel (= 6.0.3.3)
25
- activesupport (= 6.0.3.3)
26
- activesupport (6.0.3.3)
21
+ activemodel (6.0.3.2)
22
+ activesupport (= 6.0.3.2)
23
+ activerecord (6.0.3.2)
24
+ activemodel (= 6.0.3.2)
25
+ activesupport (= 6.0.3.2)
26
+ activesupport (6.0.3.2)
27
27
  concurrent-ruby (~> 1.0, >= 1.0.2)
28
28
  i18n (>= 0.7, < 2)
29
29
  minitest (~> 5.1)
@@ -39,23 +39,24 @@ GEM
39
39
  attr_required (1.0.1)
40
40
  bindata (2.4.8)
41
41
  coderay (1.1.3)
42
- concurrent-ruby (1.1.7)
43
- crack (0.4.4)
42
+ concurrent-ruby (1.1.6)
43
+ crack (0.4.3)
44
+ safe_yaml (~> 1.0.0)
44
45
  database_cleaner (1.8.5)
45
46
  database_cleaner-active_record (1.8.0)
46
47
  activerecord
47
48
  database_cleaner (~> 1.8.0)
48
49
  diff-lcs (1.4.4)
49
50
  docile (1.3.2)
50
- factory_bot (6.1.0)
51
+ factory_bot (6.0.2)
51
52
  activesupport (>= 5.0.0)
52
- faker (2.14.0)
53
+ faker (2.13.0)
53
54
  i18n (>= 1.6, < 2)
54
55
  graphql (1.11.4)
55
56
  hashdiff (1.0.1)
56
57
  hashie (4.1.0)
57
58
  httpclient (2.8.3)
58
- i18n (1.8.5)
59
+ i18n (1.8.3)
59
60
  concurrent-ruby (~> 1.0)
60
61
  json (2.3.1)
61
62
  json-jwt (1.13.0)
@@ -65,7 +66,7 @@ GEM
65
66
  jwt (2.2.2)
66
67
  method_source (1.0.0)
67
68
  mini_portile2 (2.4.0)
68
- minitest (5.14.2)
69
+ minitest (5.14.1)
69
70
  multi_json (1.15.0)
70
71
  mustermann (1.1.1)
71
72
  ruby2_keywords (~> 0.0.1)
@@ -86,7 +87,7 @@ GEM
86
87
  pry (0.13.1)
87
88
  coderay (~> 1.1)
88
89
  method_source (~> 1.0)
89
- public_suffix (4.0.6)
90
+ public_suffix (4.0.5)
90
91
  rack (2.2.3)
91
92
  rack-contrib (2.2.0)
92
93
  rack (~> 2.0)
@@ -102,7 +103,7 @@ GEM
102
103
  rack (>= 1.0, < 3)
103
104
  rainbow (3.0.0)
104
105
  rake (13.0.1)
105
- regexp_parser (1.8.0)
106
+ regexp_parser (1.7.1)
106
107
  rexml (3.2.4)
107
108
  rspec (3.9.0)
108
109
  rspec-core (~> 3.9.0)
@@ -117,21 +118,22 @@ GEM
117
118
  diff-lcs (>= 1.2.0, < 2.0)
118
119
  rspec-support (~> 3.9.0)
119
120
  rspec-support (3.9.3)
120
- rubocop (0.91.0)
121
+ rubocop (0.86.0)
121
122
  parallel (~> 1.10)
122
- parser (>= 2.7.1.1)
123
+ parser (>= 2.7.0.1)
123
124
  rainbow (>= 2.2.2, < 4.0)
124
125
  regexp_parser (>= 1.7)
125
126
  rexml
126
- rubocop-ast (>= 0.4.0, < 1.0)
127
+ rubocop-ast (>= 0.0.3, < 1.0)
127
128
  ruby-progressbar (~> 1.7)
128
129
  unicode-display_width (>= 1.4.0, < 2.0)
129
- rubocop-ast (0.4.2)
130
- parser (>= 2.7.1.4)
130
+ rubocop-ast (0.1.0)
131
+ parser (>= 2.7.0.1)
131
132
  ruby-progressbar (1.10.1)
132
133
  ruby-saml (1.11.0)
133
134
  nokogiri (>= 1.5.10)
134
135
  ruby2_keywords (0.0.2)
136
+ safe_yaml (1.0.5)
135
137
  simplecov (0.17.0)
136
138
  docile (~> 1.1)
137
139
  json (>= 1.8, < 3)
@@ -156,11 +158,11 @@ GEM
156
158
  tzinfo (1.2.7)
157
159
  thread_safe (~> 0.1)
158
160
  unicode-display_width (1.7.0)
159
- webmock (3.9.1)
161
+ webmock (3.8.3)
160
162
  addressable (>= 2.3.6)
161
163
  crack (>= 0.3.2)
162
164
  hashdiff (>= 0.4.0, < 2.0.0)
163
- zeitwerk (2.4.0)
165
+ zeitwerk (2.3.1)
164
166
 
165
167
  PLATFORMS
166
168
  ruby
@@ -4,10 +4,10 @@ module Osso
4
4
  module GraphQL
5
5
  module Types
6
6
  class IdentityProviderStatus < BaseEnum
7
- value('Pending', value: 'pending')
8
- value('Configured', value: 'configured')
9
- value('Active', value: 'active')
10
- value('Error', value: 'error')
7
+ value('Pending', value: 'PENDING')
8
+ value('Configured', value: 'CONFIGURED')
9
+ value('Active', value: 'ACTIVE')
10
+ value('Error', value: 'ERROR')
11
11
  end
12
12
  end
13
13
  end
@@ -66,7 +66,7 @@ module Osso
66
66
  end
67
67
 
68
68
  def token
69
- session['admin_token'] || request.env['HTTP_AUTHORIZATION'] || request.params['admin_token']
69
+ request.env['admin_token'] || session['admin_token'] || request['admin_token']
70
70
  end
71
71
 
72
72
  def chomp_token
@@ -6,7 +6,6 @@ module Osso
6
6
  module RouteMap
7
7
  def self.included(klass)
8
8
  klass.class_eval do
9
-
10
9
  use Osso::Admin
11
10
  use Osso::Auth
12
11
  use Osso::Oauth
@@ -13,7 +13,7 @@ module Osso
13
13
  has_many :identity_providers
14
14
 
15
15
  def single_provider?
16
- identity_providers.not_pending.one?
16
+ identity_providers.one?
17
17
  end
18
18
 
19
19
  def provider
@@ -10,8 +10,6 @@ module Osso
10
10
  before_save :set_status
11
11
  validate :sso_cert_valid
12
12
 
13
- enum status: { pending: "PENDING", configured: 'CONFIGURED', active: "ACTIVE", error: "ERROR"}
14
-
15
13
  PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"
16
14
  PEM_FOOTER = "\n-----END CERTIFICATE-----"
17
15
 
@@ -41,15 +39,17 @@ module Osso
41
39
  alias acs_url assertion_consumer_service_url
42
40
 
43
41
  def set_status
44
- self.status = 'configured' if sso_url && sso_cert && pending?
42
+ return if status != 'PENDING'
43
+
44
+ self.status = 'CONFIGURED' if sso_url && sso_cert
45
45
  end
46
46
 
47
47
  def active!
48
- update(status: 'active')
48
+ update(status: 'ACTIVE')
49
49
  end
50
50
 
51
51
  def error!
52
- update(status: 'error')
52
+ update(status: 'ERROR')
53
53
  end
54
54
 
55
55
  def root_url
@@ -16,37 +16,37 @@ module Osso
16
16
  get '/login' do
17
17
  token_protected!
18
18
 
19
- erb :admin, layout: false
19
+ erb :admin
20
20
  end
21
21
 
22
22
  get '' do
23
23
  internal_protected!
24
24
 
25
- erb :admin, layout: false
25
+ erb :admin
26
26
  end
27
27
 
28
28
  get '/enterprise' do
29
29
  token_protected!
30
30
 
31
- erb :admin, layout: false
31
+ erb :admin
32
32
  end
33
33
 
34
34
  get '/enterprise/:domain' do
35
35
  enterprise_protected!(params[:domain])
36
36
 
37
- erb :admin, layout: false
37
+ erb :admin
38
38
  end
39
39
 
40
40
  get '/config' do
41
41
  admin_protected!
42
42
 
43
- erb :admin, layout: false
43
+ erb :admin
44
44
  end
45
45
 
46
46
  get '/config/:id' do
47
47
  admin_protected!
48
48
 
49
- erb :admin, layout: false
49
+ erb :admin
50
50
  end
51
51
  end
52
52
  end
@@ -13,7 +13,7 @@ module Osso
13
13
  UUID_REGEXP =
14
14
  /[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/.
15
15
  freeze
16
-
16
+
17
17
  use OmniAuth::Builder do
18
18
  OmniAuth::MultiProvider.register(
19
19
  self,
@@ -22,39 +22,14 @@ module Osso
22
22
  path_prefix: '/auth/saml',
23
23
  callback_suffix: 'callback',
24
24
  ) do |identity_provider_id, _env|
25
- Models::IdentityProvider.
26
- not_pending.
27
- find(identity_provider_id).
25
+ Models::IdentityProvider.find(identity_provider_id).
28
26
  saml_options
29
- rescue
30
- {}
31
27
  end
32
28
  end
33
29
 
34
- OmniAuth.config.on_failure = proc do |env|
35
- OmniAuth::FailureEndpoint.new(env).redirect_to_failure
36
- end
37
-
38
- namespace '/auth' do
30
+ namespace '/auth' do # rubocop:disable Metrics/BlockLength
39
31
  get '/failure' do
40
- # ??? invalid ticket, warden throws, ugh
41
-
42
- # confirmed:
43
- # - a valid but wrong cert will throw here
44
- # (OneLogin::RubySaml::ValidationError, Fingerprint mismatch)
45
- # but an _invalid_ cert is not caught. we do validate certs on
46
- # configuration, so this may be ok
47
- #
48
- # - a valid but wrong ACS URL will throw here. the urls
49
- # are pretty complex, but it has come up
50
- #
51
- # - specifying the wrong "recipient" in your IDP. Only OL so far
52
- # (OneLogin::RubySaml::ValidationError, The response was received
53
- # at vcardme.com instead of
54
- # http://localhost:9292/auth/saml/e54a9a92-b4b5-4ea5-b0e3-b1423eb20b76/callback)
55
-
56
-
57
- @error = Osso::Error::SamlConfigError.new
32
+ @error = params[:message]
58
33
  erb :error
59
34
  end
60
35
  # Enterprise users are sent here after authenticating against
@@ -62,18 +37,48 @@ module Osso
62
37
  # and then create an authorization code for that user. The user
63
38
  # is redirected back to your application with this code
64
39
  # as a URL query param, which you then exchange for an access token.
65
- post '/saml/:provider_id/callback' do
66
- redirect_uri = SamlHandler.perform(
67
- auth_hash: env['omniauth.auth'],
68
- provider_id: params[:provider_id],
69
- session: session,
40
+ post '/saml/:id/callback' do
41
+ provider = Models::IdentityProvider.find(params[:id])
42
+ @oauth_client = provider.oauth_client
43
+
44
+ # TODO: PORC for validating attributes
45
+ attributes = env['omniauth.auth']&.
46
+ extra&.
47
+ response_object&.
48
+ attributes
49
+
50
+ user = Models::User.where(
51
+ email: attributes[:email],
52
+ idp_id: attributes[:id] || attributes[:idp_id],
53
+ ).first_or_create! do |new_user|
54
+ new_user.enterprise_account_id = provider.enterprise_account_id
55
+ new_user.identity_provider_id = provider.id
56
+ end
57
+
58
+ authorization_code = user.authorization_codes.create!(
59
+ oauth_client: @oauth_client,
60
+ redirect_uri: redirect_uri,
70
61
  )
62
+ provider.active!
71
63
 
72
- redirect(redirect_uri)
73
- rescue Osso::Error::Base => e
74
- @error = e
75
- erb :error
76
- end
64
+ redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{provider_state}")
65
+ end
66
+
67
+ def redirect_uri
68
+ return @oauth_client.primary_redirect_uri.uri if valid_idp_initiated_flow
69
+
70
+ session[:osso_oauth_redirect_uri]
71
+ end
72
+
73
+ def provider_state
74
+ return @provider_state = 'IDP_INITIATED' if valid_idp_initiated_flow
75
+
76
+ session.delete(:osso_oauth_state)
77
+ end
78
+
79
+ def valid_idp_initiated_flow
80
+ !session[:osso_oauth_redirect_uri] && !session[:osso_oauth_state]
81
+ end
77
82
  end
78
83
  end
79
84
  end
@@ -16,19 +16,28 @@ module Osso
16
16
  # Once they complete IdP login, they will be returned to the
17
17
  # redirect_uri with an authorization code parameter.
18
18
  get '/authorize' do
19
- client = find_client(params[:client_id])
20
- enterprise = find_account(domain: params[:domain], client_id: client.id)
19
+ Rack::OAuth2::Server::Authorize.new do |req, _res|
20
+ client = Models::OauthClient.find_by!(identifier: req.client_id)
21
+ session[:osso_oauth_redirect_uri] = req.verify_redirect_uri!(client.redirect_uri_values)
22
+ session[:osso_oauth_state] = params[:state]
23
+ end.call(env)
21
24
 
22
- validate_oauth_request(env)
25
+ enterprise = Models::EnterpriseAccount.
26
+ includes(:identity_providers).
27
+ find_by!(domain: params[:domain])
23
28
 
24
29
  redirect "/auth/saml/#{enterprise.provider.id}" if enterprise.single_provider?
25
30
 
26
- @providers = enterprise.identity_providers.not_pending
27
- erb :multiple_providers if @providers.count > 1
31
+ @providers = enterprise.identity_providers
32
+ erb :multiple_providers
28
33
 
29
- raise Osso::Error::MissingConfiguredIdentityProvider.new(domain: params[:domain])
30
- rescue Osso::Error::Base => e
34
+ rescue Rack::OAuth2::Server::Authorize::BadRequest => e
35
+ @error = e
36
+ erb :error
37
+ rescue ActiveRecord::RecordNotFound => e
31
38
  @error = e
39
+ @error = 'No OAuth Client exists for the provided client_id' if e.model == 'Osso::Models::OauthClient'
40
+ @error = "No Customer exists with the domain #{params[:domain]}" if e.model == 'Osso::Models::EnterpriseAccount'
32
41
  erb :error
33
42
  end
34
43
 
@@ -57,31 +66,5 @@ module Osso
57
66
  user
58
67
  end
59
68
  end
60
-
61
- private
62
-
63
- def find_account(domain:, client_id:)
64
- Models::EnterpriseAccount.
65
- includes(:identity_providers).
66
- find_by!(domain: domain, oauth_client_id: client_id)
67
- rescue ActiveRecord::RecordNotFound
68
- raise Osso::Error::NoAccountForOAuthClientError.new(domain: params[:domain])
69
- end
70
-
71
- def find_client(identifier)
72
- @client ||= Models::OauthClient.find_by!(identifier: identifier)
73
- rescue ActiveRecord::RecordNotFound
74
- raise Osso::Error::InvalidOAuthClientIdentifier
75
- end
76
-
77
- def validate_oauth_request(env)
78
- Rack::OAuth2::Server::Authorize.new do |req, _res|
79
- client = find_client(req[:client_id])
80
- session[:osso_oauth_redirect_uri] = req.verify_redirect_uri!(client.redirect_uri_values)
81
- session[:osso_oauth_state] = params[:state]
82
- end.call(env)
83
- rescue Rack::OAuth2::Server::Authorize::BadRequest
84
- raise Osso::Error::InvalidRedirectUri.new(redirect_uri: params[:redirect_uri])
85
- end
86
69
  end
87
70
  end
data/lib/osso/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.5-eta'
4
+ VERSION = '0.0.5-gamma'
5
5
  end
data/lib/osso.rb CHANGED
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- require_relative 'osso/error/error'
5
4
  require_relative 'osso/helpers/helpers'
6
5
  require_relative 'osso/lib/app_config'
7
6
  require_relative 'osso/lib/oauth2_token'
8
7
  require_relative 'osso/lib/route_map'
9
- require_relative 'osso/lib/saml_handler'
10
8
  require_relative 'osso/models/models'
11
9
  require_relative 'osso/routes/routes'
12
10
  require_relative 'osso/graphql/schema'
@@ -11,8 +11,7 @@ FactoryBot.define do
11
11
  factory :enterprise_with_okta, parent: :enterprise_account do
12
12
  after :create do |enterprise|
13
13
  create(
14
- :configured_identity_provider,
15
- service: 'OKTA',
14
+ :okta_identity_provider,
16
15
  domain: enterprise.domain,
17
16
  enterprise_account_id: enterprise.id,
18
17
  )
@@ -22,8 +21,7 @@ FactoryBot.define do
22
21
  factory :enterprise_with_azure, parent: :enterprise_account do
23
22
  after :create do |enterprise|
24
23
  create(
25
- :configured_identity_provider,
26
- service: 'AZURE',
24
+ :azure_identity_provider,
27
25
  domain: enterprise.domain,
28
26
  enterprise_account_id: enterprise.id,
29
27
  )
@@ -33,15 +31,13 @@ FactoryBot.define do
33
31
  factory :enterprise_with_multiple_providers, parent: :enterprise_account do
34
32
  after :create do |enterprise|
35
33
  create(
36
- :configured_identity_provider,
37
- service: 'OKTA',
34
+ :okta_identity_provider,
38
35
  domain: enterprise.domain,
39
36
  enterprise_account_id: enterprise.id,
40
37
  )
41
38
 
42
39
  create(
43
- :configured_identity_provider,
44
- service: 'AZURE',
40
+ :azure_identity_provider,
45
41
  domain: enterprise.domain,
46
42
  enterprise_account_id: enterprise.id,
47
43
  )
@@ -8,21 +8,13 @@ describe Osso::Helpers::Auth do
8
8
  end
9
9
 
10
10
  subject(:app) do
11
- Class.new {
12
- include Osso::Helpers::Auth
13
- }
11
+ Class.new { include Osso::Helpers::Auth }
14
12
  end
15
13
 
16
14
  describe 'with the token as a header' do
17
15
  before do
18
16
  allow_any_instance_of(subject).to receive(:request) do
19
- double('Request', env: { 'HTTP_AUTHORIZATION' => token }, post?: false)
20
- end
21
-
22
- allow_any_instance_of(subject).to receive(:session) do
23
- {
24
- admin_token: nil
25
- }
17
+ double('Request', env: { 'admin_token' => token }, post?: false)
26
18
  end
27
19
 
28
20
  allow_any_instance_of(subject).to receive(:redirect) do
@@ -95,170 +87,6 @@ describe Osso::Helpers::Auth do
95
87
  end
96
88
  end
97
89
 
98
- describe 'with the token as a parameter' do
99
- before do
100
- allow_any_instance_of(subject).to receive(:request) do
101
- double('Request', env: {}, params: { 'admin_token' => token }, post?: false)
102
- end
103
-
104
- allow_any_instance_of(subject).to receive(:session) do
105
- {
106
- admin_token: nil
107
- }
108
- end
109
-
110
- allow_any_instance_of(subject).to receive(:redirect) do
111
- false
112
- end
113
- end
114
-
115
- describe 'with an admin token' do
116
- let(:token) { encode({ scope: 'admin' }) }
117
-
118
- it 'allows #token_protected! methods' do
119
- expect(subject.new.token_protected!).to_not be(false)
120
- end
121
-
122
- it 'allows #enterprise_protected! methods' do
123
- expect(subject.new.enterprise_protected!).to_not be(false)
124
- end
125
-
126
- it 'allows #internal_protected! methods' do
127
- expect(subject.new.internal_protected!).to_not be(false)
128
- end
129
-
130
- it 'allows #admin_protected! methods' do
131
- expect(subject.new.admin_protected!).to_not be(false)
132
- end
133
- end
134
-
135
- describe 'with an internal token' do
136
- let(:token) { encode({ scope: 'internal' }) }
137
-
138
- it 'allows #token_protected! methods' do
139
- expect(subject.new.token_protected!).to_not be(false)
140
- end
141
-
142
- it 'allows #enterprise_protected! methods' do
143
- expect(subject.new.enterprise_protected!).to_not be(false)
144
- end
145
-
146
- it 'allows #internal_protected! methods' do
147
- expect(subject.new.internal_protected!).to_not be(false)
148
- end
149
-
150
- it 'allows #admin_protected! methods' do
151
- expect(subject.new.admin_protected!).to be(false)
152
- end
153
- end
154
-
155
- describe 'with an end-user token' do
156
- let(:token) { encode({ scope: 'end-user', email: 'user@example.com' }) }
157
-
158
- it 'allows #token_protected! methods' do
159
- expect(subject.new.token_protected!).to_not be(false)
160
- end
161
-
162
- it 'allows #enterprise_protected! methods for the scoped domain' do
163
- expect(subject.new.enterprise_protected!('example.com')).to_not be(false)
164
- end
165
-
166
- it 'halts #enterprise_protected! methods for the wrong scoped domain' do
167
- expect(subject.new.enterprise_protected!('foo.com')).to be(false)
168
- end
169
-
170
- it 'halts #internal_protected! methods' do
171
- expect(subject.new.internal_protected!).to be(false)
172
- end
173
-
174
- it 'halts #admin_protected! methods' do
175
- expect(subject.new.admin_protected!).to be(false)
176
- end
177
- end
178
- end
179
-
180
- describe 'with the token in session' do
181
- before do
182
- allow_any_instance_of(subject).to receive(:request) do
183
- double('Request', env: {}, params: {}, post?: false)
184
- end
185
-
186
- allow_any_instance_of(subject).to receive(:redirect) do
187
- false
188
- end
189
-
190
- allow_any_instance_of(subject).to receive(:session).and_return(
191
- {admin_token: token}.with_indifferent_access
192
- )
193
-
194
- end
195
-
196
- describe 'with an admin token' do
197
- let(:token) { encode({ scope: 'admin' }) }
198
-
199
-
200
- it 'allows #token_protected! methods' do
201
- expect(subject.new.token_protected!).to_not be(false)
202
- end
203
-
204
- it 'allows #enterprise_protected! methods' do
205
- expect(subject.new.enterprise_protected!).to_not be(false)
206
- end
207
-
208
- it 'allows #internal_protected! methods' do
209
- expect(subject.new.internal_protected!).to_not be(false)
210
- end
211
-
212
- it 'allows #admin_protected! methods' do
213
- expect(subject.new.admin_protected!).to_not be(false)
214
- end
215
- end
216
-
217
- describe 'with an internal token' do
218
- let(:token) { encode({ scope: 'internal' }) }
219
-
220
- it 'allows #token_protected! methods' do
221
- expect(subject.new.token_protected!).to_not be(false)
222
- end
223
-
224
- it 'allows #enterprise_protected! methods' do
225
- expect(subject.new.enterprise_protected!).to_not be(false)
226
- end
227
-
228
- it 'allows #internal_protected! methods' do
229
- expect(subject.new.internal_protected!).to_not be(false)
230
- end
231
-
232
- it 'allows #admin_protected! methods' do
233
- expect(subject.new.admin_protected!).to be(false)
234
- end
235
- end
236
-
237
- describe 'with an end-user token' do
238
- let(:token) { encode({ scope: 'end-user', email: 'user@example.com' }) }
239
-
240
- it 'allows #token_protected! methods' do
241
- expect(subject.new.token_protected!).to_not be(false)
242
- end
243
-
244
- it 'allows #enterprise_protected! methods for the scoped domain' do
245
- expect(subject.new.enterprise_protected!('example.com')).to_not be(false)
246
- end
247
-
248
- it 'halts #enterprise_protected! methods for the wrong scoped domain' do
249
- expect(subject.new.enterprise_protected!('foo.com')).to be(false)
250
- end
251
-
252
- it 'halts #internal_protected! methods' do
253
- expect(subject.new.internal_protected!).to be(false)
254
- end
255
-
256
- it 'halts #admin_protected! methods' do
257
- expect(subject.new.admin_protected!).to be(false)
258
- end
259
- end
260
- end
261
-
262
90
  def encode(payload)
263
91
  JWT.encode(
264
92
  payload,
@@ -3,9 +3,6 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Osso::Auth do
6
- before do
7
- described_class.set(:views, spec_views)
8
- end
9
6
  describe 'get /auth/saml/:uuid' do
10
7
  describe 'for an Okta SAML provider' do
11
8
  let(:enterprise) { create(:enterprise_with_okta) }
@@ -46,6 +43,7 @@ describe Osso::Auth do
46
43
  nil,
47
44
  {
48
45
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
46
+ 'identity_provider' => okta_provider,
49
47
  },
50
48
  )
51
49
  end.to change { Osso::Models::User.count }.by(1)
@@ -60,6 +58,7 @@ describe Osso::Auth do
60
58
  nil,
61
59
  {
62
60
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
61
+ 'identity_provider' => okta_provider,
63
62
  },
64
63
  )
65
64
  end.to change { Osso::Models::AuthorizationCode.count }.by(1)
@@ -74,6 +73,7 @@ describe Osso::Auth do
74
73
  nil,
75
74
  {
76
75
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
76
+ 'identity_provider' => okta_provider,
77
77
  },
78
78
  )
79
79
  expect(last_response).to be_redirect
@@ -99,6 +99,7 @@ describe Osso::Auth do
99
99
  nil,
100
100
  {
101
101
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
102
+ 'identity_provider' => okta_provider,
102
103
  },
103
104
  )
104
105
  end.to_not(change { Osso::Models::User.count })
@@ -109,9 +110,10 @@ describe Osso::Auth do
109
110
  nil,
110
111
  {
111
112
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
113
+ 'identity_provider' => okta_provider,
112
114
  },
113
115
  )
114
- expect(okta_provider.reload.status).to eq('active')
116
+ expect(okta_provider.reload.status).to eq('ACTIVE')
115
117
  end
116
118
  end
117
119
  end
@@ -130,6 +132,7 @@ describe Osso::Auth do
130
132
  nil,
131
133
  {
132
134
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
135
+ 'identity_provider' => azure_provider,
133
136
  },
134
137
  )
135
138
  end.to change { Osso::Models::User.count }.by(1)
@@ -143,10 +146,11 @@ describe Osso::Auth do
143
146
  nil,
144
147
  {
145
148
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
149
+ 'identity_provider' => azure_provider,
146
150
  },
147
151
  )
148
152
 
149
- expect(azure_provider.reload.status).to eq('active')
153
+ expect(azure_provider.reload.status).to eq('ACTIVE')
150
154
  end
151
155
  end
152
156
 
@@ -166,6 +170,7 @@ describe Osso::Auth do
166
170
  nil,
167
171
  {
168
172
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
173
+ 'identity_provider' => azure_provider,
169
174
  },
170
175
  )
171
176
  end.to_not(change { Osso::Models::User.count })
@@ -173,40 +178,4 @@ describe Osso::Auth do
173
178
  end
174
179
  end
175
180
  end
176
-
177
- context 'with an invalid SAML response' do
178
- describe 'post /auth/saml/:uuid/callback' do
179
- let!(:enterprise) { create(:enterprise_with_azure) }
180
- let!(:azure_provider) { enterprise.provider }
181
-
182
- it 'raises an error when email is missing' do
183
- mock_saml_omniauth(email: nil, id: SecureRandom.uuid)
184
-
185
-
186
- response = post(
187
- "/auth/saml/#{azure_provider.id}/callback",
188
- nil,
189
- {
190
- 'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
191
- },
192
- )
193
-
194
- expect(response.body).to eq('Osso::Error::MissingSamlEmailAttributeError')
195
- end
196
-
197
- it 'raises an error when id is missing' do
198
- mock_saml_omniauth(email: Faker::Internet.email, id: nil)
199
-
200
- response = post(
201
- "/auth/saml/#{azure_provider.id}/callback",
202
- nil,
203
- {
204
- 'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
205
- },
206
- )
207
-
208
- expect(response.body).to eq('Osso::Error::MissingSamlIdAttributeError')
209
- end
210
- end
211
- end
212
181
  end
@@ -1 +0,0 @@
1
- <%= @error %>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5.pre.eta
4
+ version: 0.0.5.pre.gamma
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Bauch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-22 00:00:00.000000000 Z
11
+ date: 2020-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -273,11 +273,6 @@ files:
273
273
  - lib/osso/db/migrate/20200826201852_create_app_config.rb
274
274
  - lib/osso/db/migrate/20200913154919_add_one_login_to_identity_provider_service_enum.rb
275
275
  - lib/osso/db/migrate/20200916125543_add_google_to_identity_provider_service_enum.rb
276
- - lib/osso/error/account_configuration_error.rb
277
- - lib/osso/error/error.rb
278
- - lib/osso/error/missing_saml_attribute_error.rb
279
- - lib/osso/error/oauth_error.rb
280
- - lib/osso/error/saml_config_error.rb
281
276
  - lib/osso/graphql/.DS_Store
282
277
  - lib/osso/graphql/mutation.rb
283
278
  - lib/osso/graphql/mutations.rb
@@ -318,7 +313,6 @@ files:
318
313
  - lib/osso/lib/app_config.rb
319
314
  - lib/osso/lib/oauth2_token.rb
320
315
  - lib/osso/lib/route_map.rb
321
- - lib/osso/lib/saml_handler.rb
322
316
  - lib/osso/models/access_token.rb
323
317
  - lib/osso/models/app_config.rb
324
318
  - lib/osso/models/authorization_code.rb
@@ -353,7 +347,6 @@ files:
353
347
  - spec/graphql/query/identity_provider_spec.rb
354
348
  - spec/graphql/query/oauth_clients_spec.rb
355
349
  - spec/helpers/auth_spec.rb
356
- - spec/lib/saml_handler_spec.rb
357
350
  - spec/models/identity_provider_spec.rb
358
351
  - spec/routes/admin_spec.rb
359
352
  - spec/routes/app_spec.rb
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Osso
4
- module Error
5
- class AccountConfigurationError < Base; end
6
-
7
- class MissingConfiguredIdentityProvider < AccountConfigurationError
8
- def initialize(domain: 'The requested domain')
9
- @domain = domain
10
- end
11
-
12
- def message
13
- "#{@domain} has no configured Identity Provider. " \
14
- 'SAML configuartion must be finalized before a user ' \
15
- 'for this domain can sign in with SSO.'
16
- end
17
- end
18
- end
19
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Osso
4
- module Error
5
- class Base < StandardError
6
- def docs_url
7
- nil
8
- end
9
- end
10
- end
11
- end
12
-
13
- require_relative 'account_configuration_error'
14
- require_relative 'missing_saml_attribute_error'
15
- require_relative 'oauth_error'
16
- require_relative 'saml_config_error'
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Osso
4
- module Error
5
- class MissingSamlAttributeError < Base; end
6
-
7
- class MissingSamlEmailAttributeError < MissingSamlAttributeError
8
- def message
9
- 'SAML response does not include the attribute `email`. ' \
10
- "Review the setup guide and check the attributes you're sending from your Identity Provider."
11
- end
12
- end
13
-
14
- class MissingSamlIdAttributeError < MissingSamlAttributeError
15
- def message
16
- 'SAML response does not include the attribute `id` or `idp_id`.' \
17
- "Review the setup guide and check the attributes you're sending from your Identity Provider."
18
- end
19
- end
20
- end
21
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Osso
4
- module Error
5
- class OAuthError < Base
6
- def docs_url
7
- 'https://ossoapp.com/docs/integration/oauth-consumption'
8
- end
9
- end
10
-
11
- class NoAccountForOAuthClientError < OAuthError
12
- def initialize(domain: 'the requested domain')
13
- @domain = domain
14
- end
15
-
16
- def message
17
- "No customer account exists for #{@domain} and OAuth client pair. " \
18
- "Review our OAuth documentation, and check you're using the correct OAuth client identifier. " \
19
- 'This usually suggests an engineering issue with your ENV variables.'
20
- end
21
- end
22
-
23
- class InvalidOAuthClientIdentifier < OAuthError
24
- def message
25
- 'No OAuth client exists for the requested OAuth client identifier. ' \
26
- "Review our OAuth documentation, and ensure you're using a valid OAuth client identifier. " \
27
- 'OAuth credentials may have been regenerated by your team.'
28
- end
29
- end
30
-
31
- class InvalidRedirectUri < OAuthError
32
- def initialize(redirect_uri:)
33
- @redirect_uri = redirect_uri
34
- end
35
-
36
- def message
37
- "The requested redirect URI #{@redirect_uri} is not on the allow-list for the rquested OAuth client identifier. " \
38
- "Review our OAuth documentation, check you're using the correct OAuth client identifier, " \
39
- 'and confirm your Redirect URI allow-list includes the appropriate URI(s).'
40
- end
41
- end
42
- end
43
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Osso
4
- module Error
5
- class SamlConfigError < Base
6
- def message
7
- 'Something went wrong with your SAML configuration.'
8
- end
9
- end
10
-
11
- class InvalidACSURLError < SamlConfigError
12
- def message
13
- 'The ACS URL specfied in your Identity Provider configuration is malformed.'
14
- end
15
- end
16
- end
17
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Osso
4
- class SamlHandler
5
- attr_accessor :session, :provider, :attributes
6
-
7
- def self.perform(**attrs)
8
- new(attrs).perform
9
- end
10
-
11
- def initialize(auth_hash:, provider_id:, session:)
12
- find_provider(provider_id)
13
- @attributes = auth_hash&.extra&.response_object&.attributes
14
- @session = session
15
- end
16
-
17
- def perform
18
- validate_attributes
19
- provider.active!
20
- redirect_uri
21
- end
22
-
23
- private
24
-
25
- def find_provider(id)
26
- @provider ||= Models::IdentityProvider.find(id)
27
- rescue ActiveRecord::RecordNotFound
28
- raise Osso::Error::InvalidACSURLError
29
- end
30
-
31
- def validate_attributes
32
- raise Osso::Error::MissingSamlIdAttributeError unless id_attribute
33
- raise Osso::Error::MissingSamlEmailAttributeError unless email_attribute
34
- end
35
-
36
- def id_attribute
37
- @id_attribute ||= attributes[:id] || attributes[:idp_id]
38
- end
39
-
40
- def email_attribute
41
- attributes[:email]
42
- end
43
-
44
- def user
45
- @user ||= Models::User.where(
46
- email: email_attribute,
47
- idp_id: id_attribute,
48
- ).first_or_create! do |new_user|
49
- new_user.enterprise_account_id = provider.enterprise_account_id
50
- new_user.identity_provider_id = provider.id
51
- end
52
- end
53
-
54
- def authorization_code
55
- @authorization_code ||= user.authorization_codes.create!(
56
- oauth_client: provider.oauth_client,
57
- redirect_uri: redirect_uri_base,
58
- )
59
- end
60
-
61
- def redirect_uri
62
- redirect_uri_base + redirect_uri_querystring
63
- end
64
-
65
- def redirect_uri_base
66
- return provider.oauth_client.primary_redirect_uri.uri if valid_idp_initiated_flow
67
-
68
- session[:osso_oauth_redirect_uri]
69
- end
70
-
71
- def redirect_uri_querystring
72
- "?code=#{CGI.escape(authorization_code.token)}&state=#{provider_state}"
73
- end
74
-
75
- def provider_state
76
- return 'IDP_INITIATED' if valid_idp_initiated_flow
77
-
78
- session.delete(:osso_oauth_state)
79
- end
80
-
81
- def valid_idp_initiated_flow
82
- !session[:osso_oauth_redirect_uri] && !session[:osso_oauth_state]
83
- end
84
- end
85
- end
@@ -1 +0,0 @@
1
-