osso 0.0.5.pre.gamma → 0.0.5.pre.zeta

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: f8349ed4f80afe46d642e3e1e7aaa9b9d0f90ece6e0fd6b8e0db416f74cc54f1
4
- data.tar.gz: 9aa776d8e26b04570e4123baf232404c3e9059c66bcb1fe39f2ac337c59e7edd
3
+ metadata.gz: eb48ae64d0aa5fb579751215755503e9140506ba9b75b87436fa824d7a2a18f6
4
+ data.tar.gz: e9a47f808971ce6e6e36d0f189643fb970e82656507a3de474968e3368e16c29
5
5
  SHA512:
6
- metadata.gz: aa38f696f541aa36893252c26aa1ffea8b6ac33b05b3bb130ecf7b1b71094042a7546984d67857ba8002c47744c47a1c813e7d972f38068be6c50f35a11aa9b7
7
- data.tar.gz: 515be1591f3d5ce752057483cfa8e7d94d1665a7534fe1112c5c607f7ec2417ff650367826d7eec71d64086d735994c4f1b7ac322756df103adbdb6c4958bec2
6
+ metadata.gz: 49c911667a8eacb8b90958988dd7a8c6625b8d219e6ed24612ac9a6aa5bfe6ee975f8bf0813588379a769d0314d69ee358d3d572a1f6b80ba823dd79b6505047
7
+ data.tar.gz: 03bb1c60447df03e088a40e6fbdcabeb14625ec3cd5c8944929cf0626b1e2000a057eb4daa7db77a1a0c4e79fb02c2f3efed61d4be64255a03e3d22b19dfebbe
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osso (0.0.5.pre.gamma)
4
+ osso (0.0.5.pre.zeta)
5
5
  activesupport (>= 6.0.3.2)
6
6
  graphql
7
7
  jwt
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
+ require_relative 'osso/error/error'
4
5
  require_relative 'osso/helpers/helpers'
5
6
  require_relative 'osso/lib/app_config'
6
7
  require_relative 'osso/lib/oauth2_token'
7
8
  require_relative 'osso/lib/route_map'
9
+ require_relative 'osso/lib/saml_handler'
8
10
  require_relative 'osso/models/models'
9
11
  require_relative 'osso/routes/routes'
10
12
  require_relative 'osso/graphql/schema'
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,16 @@
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'
@@ -0,0 +1,21 @@
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
@@ -0,0 +1,43 @@
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
@@ -0,0 +1,17 @@
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
@@ -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
@@ -0,0 +1,85 @@
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
@@ -13,7 +13,7 @@ module Osso
13
13
  has_many :identity_providers
14
14
 
15
15
  def single_provider?
16
- identity_providers.one?
16
+ identity_providers.not_pending.one?
17
17
  end
18
18
 
19
19
  def provider
@@ -10,6 +10,8 @@ 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
+
13
15
  PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"
14
16
  PEM_FOOTER = "\n-----END CERTIFICATE-----"
15
17
 
@@ -39,17 +41,15 @@ module Osso
39
41
  alias acs_url assertion_consumer_service_url
40
42
 
41
43
  def set_status
42
- return if status != 'PENDING'
43
-
44
- self.status = 'CONFIGURED' if sso_url && sso_cert
44
+ self.status = 'configured' if sso_url && sso_cert && pending?
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
19
+ erb :admin, layout: false
20
20
  end
21
21
 
22
22
  get '' do
23
23
  internal_protected!
24
24
 
25
- erb :admin
25
+ erb :admin, layout: false
26
26
  end
27
27
 
28
28
  get '/enterprise' do
29
29
  token_protected!
30
30
 
31
- erb :admin
31
+ erb :admin, layout: false
32
32
  end
33
33
 
34
34
  get '/enterprise/:domain' do
35
35
  enterprise_protected!(params[:domain])
36
36
 
37
- erb :admin
37
+ erb :admin, layout: false
38
38
  end
39
39
 
40
40
  get '/config' do
41
41
  admin_protected!
42
42
 
43
- erb :admin
43
+ erb :admin, layout: false
44
44
  end
45
45
 
46
46
  get '/config/:id' do
47
47
  admin_protected!
48
48
 
49
- erb :admin
49
+ erb :admin, layout: false
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,14 +22,39 @@ module Osso
22
22
  path_prefix: '/auth/saml',
23
23
  callback_suffix: 'callback',
24
24
  ) do |identity_provider_id, _env|
25
- Models::IdentityProvider.find(identity_provider_id).
25
+ Models::IdentityProvider.
26
+ not_pending.
27
+ find(identity_provider_id).
26
28
  saml_options
29
+ rescue
30
+ {}
27
31
  end
28
32
  end
29
33
 
30
- namespace '/auth' do # rubocop:disable Metrics/BlockLength
34
+ OmniAuth.config.on_failure = proc do |env|
35
+ OmniAuth::FailureEndpoint.new(env).redirect_to_failure
36
+ end
37
+
38
+ namespace '/auth' do
31
39
  get '/failure' do
32
- @error = params[:message]
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
33
58
  erb :error
34
59
  end
35
60
  # Enterprise users are sent here after authenticating against
@@ -37,48 +62,18 @@ module Osso
37
62
  # and then create an authorization code for that user. The user
38
63
  # is redirected back to your application with this code
39
64
  # as a URL query param, which you then exchange for an access token.
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,
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,
61
70
  )
62
- provider.active!
63
-
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
71
 
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
72
+ redirect(redirect_uri)
73
+ rescue Osso::Error::Base => e
74
+ @error = e
75
+ erb :error
76
+ end
82
77
  end
83
78
  end
84
79
  end
@@ -16,28 +16,19 @@ 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
- 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)
19
+ client = find_client(params[:client_id])
20
+ enterprise = find_account(domain: params[:domain], client_id: client.id)
24
21
 
25
- enterprise = Models::EnterpriseAccount.
26
- includes(:identity_providers).
27
- find_by!(domain: params[:domain])
22
+ validate_oauth_request(env)
28
23
 
29
24
  redirect "/auth/saml/#{enterprise.provider.id}" if enterprise.single_provider?
30
25
 
31
- @providers = enterprise.identity_providers
32
- erb :multiple_providers
26
+ @providers = enterprise.identity_providers.not_pending
27
+ erb :multiple_providers if @providers.count > 1
33
28
 
34
- rescue Rack::OAuth2::Server::Authorize::BadRequest => e
35
- @error = e
36
- erb :error
37
- rescue ActiveRecord::RecordNotFound => e
29
+ raise Osso::Error::MissingConfiguredIdentityProvider.new(domain: params[:domain])
30
+ rescue Osso::Error::Base => e
38
31
  @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'
41
32
  erb :error
42
33
  end
43
34
 
@@ -66,5 +57,31 @@ module Osso
66
57
  user
67
58
  end
68
59
  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
69
86
  end
70
87
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.5-gamma'
4
+ VERSION = '0.0.5-zeta'
5
5
  end
@@ -11,7 +11,8 @@ FactoryBot.define do
11
11
  factory :enterprise_with_okta, parent: :enterprise_account do
12
12
  after :create do |enterprise|
13
13
  create(
14
- :okta_identity_provider,
14
+ :configured_identity_provider,
15
+ service: 'OKTA',
15
16
  domain: enterprise.domain,
16
17
  enterprise_account_id: enterprise.id,
17
18
  )
@@ -21,7 +22,8 @@ FactoryBot.define do
21
22
  factory :enterprise_with_azure, parent: :enterprise_account do
22
23
  after :create do |enterprise|
23
24
  create(
24
- :azure_identity_provider,
25
+ :configured_identity_provider,
26
+ service: 'AZURE',
25
27
  domain: enterprise.domain,
26
28
  enterprise_account_id: enterprise.id,
27
29
  )
@@ -31,13 +33,15 @@ FactoryBot.define do
31
33
  factory :enterprise_with_multiple_providers, parent: :enterprise_account do
32
34
  after :create do |enterprise|
33
35
  create(
34
- :okta_identity_provider,
36
+ :configured_identity_provider,
37
+ service: 'OKTA',
35
38
  domain: enterprise.domain,
36
39
  enterprise_account_id: enterprise.id,
37
40
  )
38
41
 
39
42
  create(
40
- :azure_identity_provider,
43
+ :configured_identity_provider,
44
+ service: 'AZURE',
41
45
  domain: enterprise.domain,
42
46
  enterprise_account_id: enterprise.id,
43
47
  )
@@ -3,6 +3,9 @@
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
6
9
  describe 'get /auth/saml/:uuid' do
7
10
  describe 'for an Okta SAML provider' do
8
11
  let(:enterprise) { create(:enterprise_with_okta) }
@@ -43,7 +46,6 @@ describe Osso::Auth do
43
46
  nil,
44
47
  {
45
48
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
46
- 'identity_provider' => okta_provider,
47
49
  },
48
50
  )
49
51
  end.to change { Osso::Models::User.count }.by(1)
@@ -58,7 +60,6 @@ describe Osso::Auth do
58
60
  nil,
59
61
  {
60
62
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
61
- 'identity_provider' => okta_provider,
62
63
  },
63
64
  )
64
65
  end.to change { Osso::Models::AuthorizationCode.count }.by(1)
@@ -73,7 +74,6 @@ describe Osso::Auth do
73
74
  nil,
74
75
  {
75
76
  '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,7 +99,6 @@ describe Osso::Auth do
99
99
  nil,
100
100
  {
101
101
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
102
- 'identity_provider' => okta_provider,
103
102
  },
104
103
  )
105
104
  end.to_not(change { Osso::Models::User.count })
@@ -110,10 +109,9 @@ describe Osso::Auth do
110
109
  nil,
111
110
  {
112
111
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
113
- 'identity_provider' => okta_provider,
114
112
  },
115
113
  )
116
- expect(okta_provider.reload.status).to eq('ACTIVE')
114
+ expect(okta_provider.reload.status).to eq('active')
117
115
  end
118
116
  end
119
117
  end
@@ -132,7 +130,6 @@ describe Osso::Auth do
132
130
  nil,
133
131
  {
134
132
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
135
- 'identity_provider' => azure_provider,
136
133
  },
137
134
  )
138
135
  end.to change { Osso::Models::User.count }.by(1)
@@ -146,11 +143,10 @@ describe Osso::Auth do
146
143
  nil,
147
144
  {
148
145
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
149
- 'identity_provider' => azure_provider,
150
146
  },
151
147
  )
152
148
 
153
- expect(azure_provider.reload.status).to eq('ACTIVE')
149
+ expect(azure_provider.reload.status).to eq('active')
154
150
  end
155
151
  end
156
152
 
@@ -170,7 +166,6 @@ describe Osso::Auth do
170
166
  nil,
171
167
  {
172
168
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
173
- 'identity_provider' => azure_provider,
174
169
  },
175
170
  )
176
171
  end.to_not(change { Osso::Models::User.count })
@@ -178,4 +173,40 @@ describe Osso::Auth do
178
173
  end
179
174
  end
180
175
  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
181
212
  end
@@ -0,0 +1 @@
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.gamma
4
+ version: 0.0.5.pre.zeta
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-16 00:00:00.000000000 Z
11
+ date: 2020-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -273,6 +273,11 @@ 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
276
281
  - lib/osso/graphql/.DS_Store
277
282
  - lib/osso/graphql/mutation.rb
278
283
  - lib/osso/graphql/mutations.rb
@@ -313,6 +318,7 @@ files:
313
318
  - lib/osso/lib/app_config.rb
314
319
  - lib/osso/lib/oauth2_token.rb
315
320
  - lib/osso/lib/route_map.rb
321
+ - lib/osso/lib/saml_handler.rb
316
322
  - lib/osso/models/access_token.rb
317
323
  - lib/osso/models/app_config.rb
318
324
  - lib/osso/models/authorization_code.rb
@@ -347,6 +353,7 @@ files:
347
353
  - spec/graphql/query/identity_provider_spec.rb
348
354
  - spec/graphql/query/oauth_clients_spec.rb
349
355
  - spec/helpers/auth_spec.rb
356
+ - spec/lib/saml_handler_spec.rb
350
357
  - spec/models/identity_provider_spec.rb
351
358
  - spec/routes/admin_spec.rb
352
359
  - spec/routes/app_spec.rb