osso 0.0.3.27 → 0.0.5.pre.epsilon

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: 61667cd9c9821cbbd903bd5ea753dfb67338a801e442b5e7ce21d77e06e2ada6
4
- data.tar.gz: ef180637bd4da14905549a161e153e4a7b6b1dc9b9079d0e98952f7730e6d6ba
3
+ metadata.gz: 82bd613a1b8d5eb4f9c549d9d855c629698a120d2ee99bf3a52f6911fa4d080a
4
+ data.tar.gz: a67aec622f76bcc42cc16065d11a795c09893caa1115759b5739fa384108f6f2
5
5
  SHA512:
6
- metadata.gz: 59dde627b70d7f46a680dbf00ae260cd98613a679daaf14a2a08e7ce6aef7ab627ecf8b1ba8a459438b518f85e24b2db1e63375814c526f12733284fbbdc5a78
7
- data.tar.gz: '09a8f59725b4a61aad3a92e8a676377d798049da26951882903c40a8ab7e2945e153097d4e94d896dc601d1d1fc01f518d05adc58af141ce56bc4a12697f40ab'
6
+ metadata.gz: c2a1ba5df34d2e175ed7a3ab7e61dcb573b5eb9a636753f7159860f817e6b61515b9350b63a1ac1ffece994f27a7f17d61d2de05e5f6a0468168cfb7a268f879
7
+ data.tar.gz: 475eba77f824e32701ff9eae95bef4663f2e0de8d43c9d542846f6ecaec55edb1242251cb4ed50b9cb1db627252293cf406a633cc8c0c63fb7f419e10dc95e3c
@@ -0,0 +1 @@
1
+ 2.6.6
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osso (0.0.3.27)
4
+ osso (0.0.5.pre.epsilon)
5
5
  activesupport (>= 6.0.3.2)
6
6
  graphql
7
7
  jwt
@@ -31,14 +31,13 @@ GEM
31
31
  zeitwerk (~> 2.2, >= 2.2.2)
32
32
  addressable (2.7.0)
33
33
  public_suffix (>= 2.0.2, < 5.0)
34
- aes_key_wrap (1.0.1)
34
+ aes_key_wrap (1.1.0)
35
35
  annotate (3.1.1)
36
36
  activerecord (>= 3.2, < 7.0)
37
37
  rake (>= 10.4, < 14.0)
38
38
  ast (2.4.1)
39
39
  attr_required (1.0.1)
40
- backports (3.18.1)
41
- bindata (2.4.7)
40
+ bindata (2.4.8)
42
41
  coderay (1.1.3)
43
42
  concurrent-ruby (1.1.6)
44
43
  crack (0.4.3)
@@ -53,7 +52,7 @@ GEM
53
52
  activesupport (>= 5.0.0)
54
53
  faker (2.13.0)
55
54
  i18n (>= 1.6, < 2)
56
- graphql (1.11.1)
55
+ graphql (1.11.4)
57
56
  hashdiff (1.0.1)
58
57
  hashie (4.1.0)
59
58
  httpclient (2.8.3)
@@ -64,23 +63,23 @@ GEM
64
63
  activesupport (>= 4.2)
65
64
  aes_key_wrap
66
65
  bindata
67
- jwt (2.2.1)
66
+ jwt (2.2.2)
68
67
  method_source (1.0.0)
69
68
  mini_portile2 (2.4.0)
70
69
  minitest (5.14.1)
71
70
  multi_json (1.15.0)
72
71
  mustermann (1.1.1)
73
72
  ruby2_keywords (~> 0.0.1)
74
- nokogiri (1.10.9)
73
+ nokogiri (1.10.10)
75
74
  mini_portile2 (~> 2.4.0)
76
75
  omniauth (1.9.1)
77
76
  hashie (>= 3.4.6)
78
77
  rack (>= 1.6.2, < 3)
79
78
  omniauth-multi-provider (0.2.1)
80
79
  omniauth
81
- omniauth-saml (1.10.1)
80
+ omniauth-saml (1.10.2)
82
81
  omniauth (~> 1.3, >= 1.3.2)
83
- ruby-saml (~> 1.7)
82
+ ruby-saml (~> 1.9)
84
83
  parallel (1.19.2)
85
84
  parser (2.7.1.4)
86
85
  ast (~> 2.4.1)
@@ -92,13 +91,13 @@ GEM
92
91
  rack (2.2.3)
93
92
  rack-contrib (2.2.0)
94
93
  rack (~> 2.0)
95
- rack-oauth2 (1.14.0)
94
+ rack-oauth2 (1.16.0)
96
95
  activesupport
97
96
  attr_required
98
97
  httpclient
99
98
  json-jwt (>= 1.11.0)
100
99
  rack (>= 2.1.0)
101
- rack-protection (2.0.8.1)
100
+ rack-protection (2.1.0)
102
101
  rack
103
102
  rack-test (1.1.0)
104
103
  rack (>= 1.0, < 3)
@@ -140,20 +139,19 @@ GEM
140
139
  json (>= 1.8, < 3)
141
140
  simplecov-html (~> 0.10.0)
142
141
  simplecov-html (0.10.2)
143
- sinatra (2.0.8.1)
142
+ sinatra (2.1.0)
144
143
  mustermann (~> 1.0)
145
- rack (~> 2.0)
146
- rack-protection (= 2.0.8.1)
144
+ rack (~> 2.2)
145
+ rack-protection (= 2.1.0)
147
146
  tilt (~> 2.0)
148
147
  sinatra-activerecord (2.0.18)
149
148
  activerecord (>= 4.1)
150
149
  sinatra (>= 1.0)
151
- sinatra-contrib (2.0.8.1)
152
- backports (>= 2.8.2)
150
+ sinatra-contrib (2.1.0)
153
151
  multi_json
154
152
  mustermann (~> 1.0)
155
- rack-protection (= 2.0.8.1)
156
- sinatra (= 2.0.8.1)
153
+ rack-protection (= 2.1.0)
154
+ sinatra (= 2.1.0)
157
155
  tilt (~> 2.0)
158
156
  thread_safe (0.3.6)
159
157
  tilt (2.0.10)
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 2020_08_26_201852) do
13
+ ActiveRecord::Schema.define(version: 2020_09_13_154919) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "pgcrypto"
@@ -62,7 +62,7 @@ ActiveRecord::Schema.define(version: 2020_08_26_201852) do
62
62
  end
63
63
 
64
64
  # Could not dump table "identity_providers" because of following StandardError
65
- # Unknown type 'identity_provider_status' for column 'status'
65
+ # Unknown type 'identity_provider_service' for column 'service'
66
66
 
67
67
  create_table "oauth_clients", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
68
68
  t.string "name", null: false
@@ -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'
@@ -1,17 +1,13 @@
1
1
  class AddIdentityProviderServiceEnum < ActiveRecord::Migration[6.0]
2
- def change
3
- def up
4
- execute <<-SQL
5
- CREATE TYPE identity_provider_service AS ENUM ('OKTA', 'AZURE');
6
- SQL
7
- change_column :identity_providers, :service, :identity_provider_service
8
- end
9
-
10
- def down
11
- chnage_column :identity_providers, :service, :text
12
- execute <<-SQL
13
- DROP TYPE identity_provider_service;
14
- SQL
15
- end
2
+ def up
3
+ execute "CREATE TYPE identity_provider_service AS ENUM ('OKTA', 'AZURE');"
4
+ change_column :identity_providers, :service, 'identity_provider_service USING CAST(service as identity_provider_service)'
5
+ end
6
+
7
+ def down
8
+ chnage_column :identity_providers, :service, :text
9
+ execute <<-SQL
10
+ DROP TYPE identity_provider_service;
11
+ SQL
16
12
  end
17
13
  end
@@ -0,0 +1,28 @@
1
+ class AddOneLoginToIdentityProviderServiceEnum < ActiveRecord::Migration[6.0]
2
+ disable_ddl_transaction!
3
+
4
+ def up
5
+ execute <<-SQL
6
+ ALTER TYPE identity_provider_service ADD VALUE 'ONELOGIN';
7
+ SQL
8
+ end
9
+
10
+ def down
11
+ execute <<~SQL
12
+ CREATE TYPE identity_provider_service_new AS ENUM ('AZURE', 'OKTA');
13
+
14
+ -- Remove values that won't be compatible with new definition
15
+ DELETE FROM identity_providers WHERE service = 'ONELOGIN';
16
+
17
+ -- Convert to new type, casting via text representation
18
+ ALTER TABLE identity_providers
19
+ ALTER COLUMN service TYPE identity_provider_service_new
20
+ USING (service::text::identity_provider_service_new);
21
+
22
+ -- and swap the types
23
+ DROP TYPE identity_provider_service;
24
+
25
+ ALTER TYPE identity_provider_service_new RENAME TO identity_provider_service;
26
+ SQL
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ class AddGoogleToIdentityProviderServiceEnum < ActiveRecord::Migration[6.0]
2
+ disable_ddl_transaction!
3
+
4
+ def up
5
+ execute <<-SQL
6
+ ALTER TYPE identity_provider_service ADD VALUE 'GOOGLE';
7
+ SQL
8
+ end
9
+
10
+ def down
11
+ execute <<~SQL
12
+ CREATE TYPE identity_provider_service_new AS ENUM ('AZURE', 'OKTA', 'ONELOGIN');
13
+
14
+ -- Remove values that won't be compatible with new definition
15
+ DELETE FROM identity_providers WHERE service = 'GOOGLE';
16
+
17
+ -- Convert to new type, casting via text representation
18
+ ALTER TABLE identity_providers
19
+ ALTER COLUMN service TYPE identity_provider_service_new
20
+ USING (service::text::identity_provider_service_new);
21
+
22
+ -- and swap the types
23
+ DROP TYPE identity_provider_service;
24
+
25
+ ALTER TYPE identity_provider_service_new RENAME TO identity_provider_service;
26
+ SQL
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Error
5
+ end
6
+ end
7
+
8
+ require_relative 'missing_saml_attribute_error'
9
+ require_relative 'oauth_error'
10
+ require_relative 'saml_config_error'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Error
5
+ class MissingSamlAttributeError < StandardError; 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,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Error
5
+ class OAuthError < StandardError; end
6
+
7
+ class NoAccountForOAuthClientError < OAuthError
8
+ def message
9
+ 'No customer account exists for the requested domain and OAuth client pair.' \
10
+ "Review our OAuth documentation, and check you're using the correct OAuth client identifier"
11
+ end
12
+ end
13
+
14
+ class InvalidOAuthClientIdentifier < MissingSamlAttributeError
15
+ def message
16
+ 'No OAuth client exists for the requested OAuth client identifier.' \
17
+ "Review our OAuth documentation, and check you're using the correct OAuth client identifier"
18
+ end
19
+ end
20
+
21
+ class InvalidRedirectUri < MissingSamlAttributeError
22
+ def message
23
+ 'Invalid Redirect URI for the requested OAuth client identifier.' \
24
+ "Review our OAuth documentation, check you're using the correct OAuth client identifier " \
25
+ 'and confirm your Redirect URI allow list.'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Error
5
+ class SamlConfigError < StandardError; end
6
+
7
+ class InvalidACSURLError < SamlConfigError
8
+ def message
9
+ 'The ACS URL specfied in your Identity Provider configuration is malformed.'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -16,7 +16,7 @@ module Osso
16
16
  'Mutation error',
17
17
  extensions: {
18
18
  'errors' => field_errors(errors),
19
- }
19
+ },
20
20
  )
21
21
  end
22
22
 
@@ -16,7 +16,7 @@ module Osso
16
16
  provider = identity_provider(**args)
17
17
 
18
18
  return response_data(identity_provider: provider) if provider.update(args)
19
-
19
+
20
20
  response_error(provider.errors)
21
21
  end
22
22
 
@@ -16,11 +16,6 @@ module Osso
16
16
  field :sso_url, String, null: true
17
17
  field :sso_cert, String, null: true
18
18
  field :status, Types::IdentityProviderStatus, null: false
19
- field :documentation_pdf_url, String, null: true
20
-
21
- def documentation_pdf_url
22
- ENV['BASE_URL'] + '/identity_provider/documentation/' + @object.id
23
- end
24
19
  end
25
20
  end
26
21
  end
@@ -4,8 +4,10 @@ module Osso
4
4
  module GraphQL
5
5
  module Types
6
6
  class IdentityProviderService < BaseEnum
7
- value('AZURE', 'Microsoft Azure Identity Provider', value: 'Osso::Models::AzureSamlProvider')
8
- value('OKTA', 'Okta Identity Provider', value: 'Osso::Models::OktaSamlProvider')
7
+ value('AZURE', 'Microsoft Azure Identity Provider', value: 'AZURE')
8
+ value('OKTA', 'Okta Identity Provider', value: 'OKTA')
9
+ value('ONELOGIN', 'OneLogin Identity Provider', value: 'ONELOGIN')
10
+ value('GOOGLE', 'Google SAML Identity Provider', value: 'GOOGLE')
9
11
  end
10
12
  end
11
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
@@ -69,7 +69,6 @@ module Osso
69
69
  else
70
70
  OpenSSL::X509::Certificate.new([PEM_HEADER, sso_cert, PEM_FOOTER].join)
71
71
  end
72
-
73
72
  rescue OpenSSL::X509::CertificateError
74
73
  errors.add(:sso_cert, 'x509 Certificate is malformed')
75
74
  end
@@ -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
@@ -27,9 +27,16 @@ module Osso
27
27
  end
28
28
  end
29
29
 
30
- namespace '/auth' do # rubocop:disable Metrics/BlockLength
30
+ OmniAuth.config.on_failure = proc do |env|
31
+ OmniAuth::FailureEndpoint.new(env).redirect_to_failure
32
+ end
33
+
34
+ error do
35
+ erb :error
36
+ end
37
+
38
+ namespace '/auth' do
31
39
  get '/failure' do
32
- @error = params[:message]
33
40
  erb :error
34
41
  end
35
42
  # Enterprise users are sent here after authenticating against
@@ -37,47 +44,17 @@ module Osso
37
44
  # and then create an authorization code for that user. The user
38
45
  # is redirected back to your application with this code
39
46
  # 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],
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,
47
+ post '/saml/:provider_id/callback' do
48
+ redirect_uri = SamlHandler.perform(
49
+ auth_hash: env['omniauth.auth'],
50
+ provider_id: params[:provider_id],
51
+ session: session,
61
52
  )
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
53
 
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]
54
+ redirect(redirect_uri)
55
+ rescue Osso::Error::InvalidACSURLError => e
56
+ @error = e
57
+ erb :error
81
58
  end
82
59
  end
83
60
  end
@@ -16,28 +16,18 @@ 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
- # TODO: multiple provider support
32
- # erb :multiple_providers
26
+ @providers = enterprise.identity_providers
27
+ erb :multiple_providers
33
28
 
34
- rescue Rack::OAuth2::Server::Authorize::BadRequest => e
35
- @error = e
36
- erb :error
37
- rescue ActiveRecord::RecordNotFound => e
29
+ rescue Osso::Error::OAuthError => e
38
30
  @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
31
  erb :error
42
32
  end
43
33
 
@@ -66,5 +56,31 @@ module Osso
66
56
  user
67
57
  end
68
58
  end
59
+
60
+ private
61
+
62
+ def find_account(domain:, client_id:)
63
+ Models::EnterpriseAccount.
64
+ includes(:identity_providers).
65
+ find_by!(domain: domain, oauth_client_id: client_id)
66
+ rescue ActiveRecord::RecordNotFound
67
+ raise Osso::Error::NoAccountForOAuthClientError
68
+ end
69
+
70
+ def find_client(identifier)
71
+ @client ||= Models::OauthClient.find_by!(identifier: identifier)
72
+ rescue ActiveRecord::RecordNotFound
73
+ raise Osso::Error::InvalidOAuthClientIdentifier
74
+ end
75
+
76
+ def validate_oauth_request(env)
77
+ Rack::OAuth2::Server::Authorize.new do |req, _res|
78
+ client = find_client(req[:client_id])
79
+ session[:osso_oauth_redirect_uri] = req.verify_redirect_uri!(client.redirect_uri_values)
80
+ session[:osso_oauth_state] = params[:state]
81
+ end.call(env)
82
+ rescue Rack::OAuth2::Server::Authorize::BadRequest
83
+ raise Osso::Error::InvalidRedirectUri
84
+ end
69
85
  end
70
86
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.3.27'
4
+ VERSION = '0.0.5-epsilon'
5
5
  end
@@ -35,7 +35,7 @@ describe Osso::Models::IdentityProvider do
35
35
  )
36
36
  end
37
37
  end
38
-
38
+
39
39
  describe '#validate_sso_cert' do
40
40
  it 'rejects an invalid cert' do
41
41
  subject.update(sso_cert: 'bad-cert')
@@ -43,7 +43,6 @@ describe Osso::Auth do
43
43
  nil,
44
44
  {
45
45
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
46
- 'identity_provider' => okta_provider,
47
46
  },
48
47
  )
49
48
  end.to change { Osso::Models::User.count }.by(1)
@@ -58,7 +57,6 @@ describe Osso::Auth do
58
57
  nil,
59
58
  {
60
59
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
61
- 'identity_provider' => okta_provider,
62
60
  },
63
61
  )
64
62
  end.to change { Osso::Models::AuthorizationCode.count }.by(1)
@@ -73,7 +71,6 @@ describe Osso::Auth do
73
71
  nil,
74
72
  {
75
73
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
76
- 'identity_provider' => okta_provider,
77
74
  },
78
75
  )
79
76
  expect(last_response).to be_redirect
@@ -99,7 +96,6 @@ describe Osso::Auth do
99
96
  nil,
100
97
  {
101
98
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
102
- 'identity_provider' => okta_provider,
103
99
  },
104
100
  )
105
101
  end.to_not(change { Osso::Models::User.count })
@@ -110,7 +106,6 @@ describe Osso::Auth do
110
106
  nil,
111
107
  {
112
108
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
113
- 'identity_provider' => okta_provider,
114
109
  },
115
110
  )
116
111
  expect(okta_provider.reload.status).to eq('ACTIVE')
@@ -132,7 +127,6 @@ describe Osso::Auth do
132
127
  nil,
133
128
  {
134
129
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
135
- 'identity_provider' => azure_provider,
136
130
  },
137
131
  )
138
132
  end.to change { Osso::Models::User.count }.by(1)
@@ -146,7 +140,6 @@ describe Osso::Auth do
146
140
  nil,
147
141
  {
148
142
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
149
- 'identity_provider' => azure_provider,
150
143
  },
151
144
  )
152
145
 
@@ -170,7 +163,6 @@ describe Osso::Auth do
170
163
  nil,
171
164
  {
172
165
  'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
173
- 'identity_provider' => azure_provider,
174
166
  },
175
167
  )
176
168
  end.to_not(change { Osso::Models::User.count })
@@ -178,4 +170,39 @@ describe Osso::Auth do
178
170
  end
179
171
  end
180
172
  end
173
+
174
+ context 'with an invalid SAML response' do
175
+ describe 'post /auth/saml/:uuid/callback' do
176
+ let!(:enterprise) { create(:enterprise_with_azure) }
177
+ let!(:azure_provider) { enterprise.provider }
178
+
179
+ it 'raises an error when email is missing' do
180
+ mock_saml_omniauth(email: nil, id: SecureRandom.uuid)
181
+
182
+ expect do
183
+ post(
184
+ "/auth/saml/#{azure_provider.id}/callback",
185
+ nil,
186
+ {
187
+ 'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
188
+ },
189
+ )
190
+ end.to raise_error(Osso::Error::MissingSamlEmailAttributeError)
191
+ end
192
+
193
+ it 'raises an error when id is missing' do
194
+ mock_saml_omniauth(email: Faker::Internet.email, id: nil)
195
+
196
+ expect do
197
+ post(
198
+ "/auth/saml/#{azure_provider.id}/callback",
199
+ nil,
200
+ {
201
+ 'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
202
+ },
203
+ )
204
+ end.to raise_error(Osso::Error::MissingSamlIdAttributeError)
205
+ end
206
+ end
207
+ end
181
208
  end
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.3.27
4
+ version: 0.0.5.pre.epsilon
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-02 00:00:00.000000000 Z
11
+ date: 2020-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -238,6 +238,7 @@ files:
238
238
  - ".gitignore"
239
239
  - ".rspec"
240
240
  - ".rubocop.yml"
241
+ - ".ruby-version"
241
242
  - CODE_OF_CONDUCT.md
242
243
  - Gemfile
243
244
  - Gemfile.lock
@@ -270,6 +271,12 @@ files:
270
271
  - lib/osso/db/migrate/20200723153750_add_missing_timestamps.rb
271
272
  - lib/osso/db/migrate/20200723162228_drop_unneeded_tables.rb
272
273
  - lib/osso/db/migrate/20200826201852_create_app_config.rb
274
+ - lib/osso/db/migrate/20200913154919_add_one_login_to_identity_provider_service_enum.rb
275
+ - lib/osso/db/migrate/20200916125543_add_google_to_identity_provider_service_enum.rb
276
+ - lib/osso/error/error.rb
277
+ - lib/osso/error/missing_saml_attribute_error.rb
278
+ - lib/osso/error/oauth_error.rb
279
+ - lib/osso/error/saml_config_error.rb
273
280
  - lib/osso/graphql/.DS_Store
274
281
  - lib/osso/graphql/mutation.rb
275
282
  - lib/osso/graphql/mutations.rb
@@ -310,6 +317,7 @@ files:
310
317
  - lib/osso/lib/app_config.rb
311
318
  - lib/osso/lib/oauth2_token.rb
312
319
  - lib/osso/lib/route_map.rb
320
+ - lib/osso/lib/saml_handler.rb
313
321
  - lib/osso/models/access_token.rb
314
322
  - lib/osso/models/app_config.rb
315
323
  - lib/osso/models/authorization_code.rb
@@ -324,8 +332,6 @@ files:
324
332
  - lib/osso/routes/auth.rb
325
333
  - lib/osso/routes/oauth.rb
326
334
  - lib/osso/routes/routes.rb
327
- - lib/osso/routes/views/error.erb
328
- - lib/osso/routes/views/multiple_providers.erb
329
335
  - lib/osso/version.rb
330
336
  - lib/tasks/bootstrap.rake
331
337
  - osso-rb.gemspec
@@ -346,6 +352,7 @@ files:
346
352
  - spec/graphql/query/identity_provider_spec.rb
347
353
  - spec/graphql/query/oauth_clients_spec.rb
348
354
  - spec/helpers/auth_spec.rb
355
+ - spec/lib/saml_handler_spec.rb
349
356
  - spec/models/identity_provider_spec.rb
350
357
  - spec/routes/admin_spec.rb
351
358
  - spec/routes/app_spec.rb
@@ -356,6 +363,7 @@ files:
356
363
  - spec/support/spec_app.rb
357
364
  - spec/support/views/admin.erb
358
365
  - spec/support/views/error.erb
366
+ - spec/support/views/multiple_providers.erb
359
367
  homepage: https://github.com/enterprise-oss/osso-rb
360
368
  licenses:
361
369
  - MIT
@@ -371,9 +379,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
371
379
  version: 2.3.0
372
380
  required_rubygems_version: !ruby/object:Gem::Requirement
373
381
  requirements:
374
- - - ">="
382
+ - - ">"
375
383
  - !ruby/object:Gem::Version
376
- version: '0'
384
+ version: 1.3.1
377
385
  requirements: []
378
386
  rubygems_version: 3.0.3
379
387
  signing_key:
@@ -1 +0,0 @@
1
- <%= @error %>
@@ -1 +0,0 @@
1
- multiple providers