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 +4 -4
- data/.ruby-version +1 -0
- data/Gemfile.lock +16 -18
- data/db/schema.rb +2 -2
- data/lib/osso.rb +2 -0
- data/lib/osso/db/migrate/20200714223226_add_identity_provider_service_enum.rb +10 -14
- data/lib/osso/db/migrate/20200913154919_add_one_login_to_identity_provider_service_enum.rb +28 -0
- data/lib/osso/db/migrate/20200916125543_add_google_to_identity_provider_service_enum.rb +28 -0
- data/lib/osso/error/error.rb +10 -0
- data/lib/osso/error/missing_saml_attribute_error.rb +21 -0
- data/lib/osso/error/oauth_error.rb +29 -0
- data/lib/osso/error/saml_config_error.rb +13 -0
- data/lib/osso/graphql/mutations/base_mutation.rb +1 -1
- data/lib/osso/graphql/mutations/configure_identity_provider.rb +1 -1
- data/lib/osso/graphql/types/identity_provider.rb +0 -5
- data/lib/osso/graphql/types/identity_provider_service.rb +4 -2
- data/lib/osso/lib/saml_handler.rb +85 -0
- data/lib/osso/models/identity_provider.rb +0 -1
- data/lib/osso/routes/admin.rb +6 -6
- data/lib/osso/routes/auth.rb +18 -41
- data/lib/osso/routes/oauth.rb +32 -16
- data/lib/osso/version.rb +1 -1
- data/spec/lib/saml_handler_spec.rb +1 -0
- data/spec/models/identity_provider_spec.rb +1 -1
- data/spec/routes/auth_spec.rb +35 -8
- data/spec/support/views/multiple_providers.erb +0 -0
- metadata +14 -6
- data/lib/osso/routes/views/error.erb +0 -1
- data/lib/osso/routes/views/multiple_providers.erb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82bd613a1b8d5eb4f9c549d9d855c629698a120d2ee99bf3a52f6911fa4d080a
|
4
|
+
data.tar.gz: a67aec622f76bcc42cc16065d11a795c09893caa1115759b5739fa384108f6f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2a1ba5df34d2e175ed7a3ab7e61dcb573b5eb9a636753f7159860f817e6b61515b9350b63a1ac1ffece994f27a7f17d61d2de05e5f6a0468168cfb7a268f879
|
7
|
+
data.tar.gz: 475eba77f824e32701ff9eae95bef4663f2e0de8d43c9d542846f6ecaec55edb1242251cb4ed50b9cb1db627252293cf406a633cc8c0c63fb7f419e10dc95e3c
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.6
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
osso (0.0.
|
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
|
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
|
-
|
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.
|
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.
|
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.
|
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.
|
80
|
+
omniauth-saml (1.10.2)
|
82
81
|
omniauth (~> 1.3, >= 1.3.2)
|
83
|
-
ruby-saml (~> 1.
|
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.
|
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
|
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
|
142
|
+
sinatra (2.1.0)
|
144
143
|
mustermann (~> 1.0)
|
145
|
-
rack (~> 2.
|
146
|
-
rack-protection (= 2.0
|
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
|
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
|
156
|
-
sinatra (= 2.0
|
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)
|
data/db/schema.rb
CHANGED
@@ -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:
|
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 '
|
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
|
data/lib/osso.rb
CHANGED
@@ -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
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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,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,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: '
|
8
|
-
value('OKTA', 'Okta Identity Provider', value: '
|
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
|
data/lib/osso/routes/admin.rb
CHANGED
@@ -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
|
data/lib/osso/routes/auth.rb
CHANGED
@@ -27,9 +27,16 @@ module Osso
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
|
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/:
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
data/lib/osso/routes/oauth.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
32
|
-
|
26
|
+
@providers = enterprise.identity_providers
|
27
|
+
erb :multiple_providers
|
33
28
|
|
34
|
-
rescue
|
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
|
data/lib/osso/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/spec/routes/auth_spec.rb
CHANGED
@@ -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
|
File without changes
|
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.
|
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-
|
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:
|
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
|