osso 0.0.4 → 0.0.5.pre.eta
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/Gemfile.lock +22 -24
- data/lib/osso.rb +2 -0
- data/lib/osso/db/migrate/20200916125543_add_google_to_identity_provider_service_enum.rb +28 -0
- data/lib/osso/error/account_configuration_error.rb +19 -0
- data/lib/osso/error/error.rb +16 -0
- data/lib/osso/error/missing_saml_attribute_error.rb +21 -0
- data/lib/osso/error/oauth_error.rb +43 -0
- data/lib/osso/error/saml_config_error.rb +17 -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_service.rb +1 -0
- data/lib/osso/graphql/types/identity_provider_status.rb +4 -4
- data/lib/osso/helpers/auth.rb +1 -1
- data/lib/osso/lib/route_map.rb +1 -0
- data/lib/osso/lib/saml_handler.rb +85 -0
- data/lib/osso/models/enterprise_account.rb +1 -1
- data/lib/osso/models/identity_provider.rb +5 -6
- data/lib/osso/routes/admin.rb +6 -6
- data/lib/osso/routes/auth.rb +39 -44
- data/lib/osso/routes/oauth.rb +33 -16
- data/lib/osso/version.rb +1 -1
- data/spec/factories/enterprise_account.rb +8 -4
- data/spec/helpers/auth_spec.rb +174 -2
- data/spec/lib/saml_handler_spec.rb +1 -0
- data/spec/models/identity_provider_spec.rb +1 -1
- data/spec/routes/auth_spec.rb +41 -10
- data/spec/support/views/error.erb +1 -0
- data/spec/support/views/multiple_providers.erb +0 -0
- metadata +13 -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: 914a99a42c898e69186b33f4c51069f8c6f4ceddd7c300ce4158f560cc9d7937
|
4
|
+
data.tar.gz: 75ece89829bdb5a756340e9c9ff63e414ec093f402d725783a8668459ea32665
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4117bbe85a6f88e7703eaf22464d2b343ac4517ec5e6d4a473a7f758ee1733a6b1f65504f13105ec9f823ae2e1f21db0129f0e9aab98ac260175decc5710aaec
|
7
|
+
data.tar.gz: 05ed7bea73e54d8ce07b8d6b494724b0cc2dc9e06cee78ba2977e249a983c3b579fe847a9ae731806b94ad94b765bf1d2d8c8b9e763245acdf6e783d40d8bbcf
|
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.eta)
|
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.
|
22
|
-
activesupport (= 6.0.3.
|
23
|
-
activerecord (6.0.3.
|
24
|
-
activemodel (= 6.0.3.
|
25
|
-
activesupport (= 6.0.3.
|
26
|
-
activesupport (6.0.3.
|
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)
|
27
27
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
28
28
|
i18n (>= 0.7, < 2)
|
29
29
|
minitest (~> 5.1)
|
@@ -39,24 +39,23 @@ 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.
|
43
|
-
crack (0.4.
|
44
|
-
safe_yaml (~> 1.0.0)
|
42
|
+
concurrent-ruby (1.1.7)
|
43
|
+
crack (0.4.4)
|
45
44
|
database_cleaner (1.8.5)
|
46
45
|
database_cleaner-active_record (1.8.0)
|
47
46
|
activerecord
|
48
47
|
database_cleaner (~> 1.8.0)
|
49
48
|
diff-lcs (1.4.4)
|
50
49
|
docile (1.3.2)
|
51
|
-
factory_bot (6.0
|
50
|
+
factory_bot (6.1.0)
|
52
51
|
activesupport (>= 5.0.0)
|
53
|
-
faker (2.
|
52
|
+
faker (2.14.0)
|
54
53
|
i18n (>= 1.6, < 2)
|
55
54
|
graphql (1.11.4)
|
56
55
|
hashdiff (1.0.1)
|
57
56
|
hashie (4.1.0)
|
58
57
|
httpclient (2.8.3)
|
59
|
-
i18n (1.8.
|
58
|
+
i18n (1.8.5)
|
60
59
|
concurrent-ruby (~> 1.0)
|
61
60
|
json (2.3.1)
|
62
61
|
json-jwt (1.13.0)
|
@@ -66,7 +65,7 @@ GEM
|
|
66
65
|
jwt (2.2.2)
|
67
66
|
method_source (1.0.0)
|
68
67
|
mini_portile2 (2.4.0)
|
69
|
-
minitest (5.14.
|
68
|
+
minitest (5.14.2)
|
70
69
|
multi_json (1.15.0)
|
71
70
|
mustermann (1.1.1)
|
72
71
|
ruby2_keywords (~> 0.0.1)
|
@@ -87,7 +86,7 @@ GEM
|
|
87
86
|
pry (0.13.1)
|
88
87
|
coderay (~> 1.1)
|
89
88
|
method_source (~> 1.0)
|
90
|
-
public_suffix (4.0.
|
89
|
+
public_suffix (4.0.6)
|
91
90
|
rack (2.2.3)
|
92
91
|
rack-contrib (2.2.0)
|
93
92
|
rack (~> 2.0)
|
@@ -103,7 +102,7 @@ GEM
|
|
103
102
|
rack (>= 1.0, < 3)
|
104
103
|
rainbow (3.0.0)
|
105
104
|
rake (13.0.1)
|
106
|
-
regexp_parser (1.
|
105
|
+
regexp_parser (1.8.0)
|
107
106
|
rexml (3.2.4)
|
108
107
|
rspec (3.9.0)
|
109
108
|
rspec-core (~> 3.9.0)
|
@@ -118,22 +117,21 @@ GEM
|
|
118
117
|
diff-lcs (>= 1.2.0, < 2.0)
|
119
118
|
rspec-support (~> 3.9.0)
|
120
119
|
rspec-support (3.9.3)
|
121
|
-
rubocop (0.
|
120
|
+
rubocop (0.91.0)
|
122
121
|
parallel (~> 1.10)
|
123
|
-
parser (>= 2.7.
|
122
|
+
parser (>= 2.7.1.1)
|
124
123
|
rainbow (>= 2.2.2, < 4.0)
|
125
124
|
regexp_parser (>= 1.7)
|
126
125
|
rexml
|
127
|
-
rubocop-ast (>= 0.0
|
126
|
+
rubocop-ast (>= 0.4.0, < 1.0)
|
128
127
|
ruby-progressbar (~> 1.7)
|
129
128
|
unicode-display_width (>= 1.4.0, < 2.0)
|
130
|
-
rubocop-ast (0.
|
131
|
-
parser (>= 2.7.
|
129
|
+
rubocop-ast (0.4.2)
|
130
|
+
parser (>= 2.7.1.4)
|
132
131
|
ruby-progressbar (1.10.1)
|
133
132
|
ruby-saml (1.11.0)
|
134
133
|
nokogiri (>= 1.5.10)
|
135
134
|
ruby2_keywords (0.0.2)
|
136
|
-
safe_yaml (1.0.5)
|
137
135
|
simplecov (0.17.0)
|
138
136
|
docile (~> 1.1)
|
139
137
|
json (>= 1.8, < 3)
|
@@ -158,11 +156,11 @@ GEM
|
|
158
156
|
tzinfo (1.2.7)
|
159
157
|
thread_safe (~> 0.1)
|
160
158
|
unicode-display_width (1.7.0)
|
161
|
-
webmock (3.
|
159
|
+
webmock (3.9.1)
|
162
160
|
addressable (>= 2.3.6)
|
163
161
|
crack (>= 0.3.2)
|
164
162
|
hashdiff (>= 0.4.0, < 2.0.0)
|
165
|
-
zeitwerk (2.
|
163
|
+
zeitwerk (2.4.0)
|
166
164
|
|
167
165
|
PLATFORMS
|
168
166
|
ruby
|
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'
|
@@ -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,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
|
@@ -7,6 +7,7 @@ module Osso
|
|
7
7
|
value('AZURE', 'Microsoft Azure Identity Provider', value: 'AZURE')
|
8
8
|
value('OKTA', 'Okta Identity Provider', value: 'OKTA')
|
9
9
|
value('ONELOGIN', 'OneLogin Identity Provider', value: 'ONELOGIN')
|
10
|
+
value('GOOGLE', 'Google SAML Identity Provider', value: 'GOOGLE')
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
@@ -4,10 +4,10 @@ module Osso
|
|
4
4
|
module GraphQL
|
5
5
|
module Types
|
6
6
|
class IdentityProviderStatus < BaseEnum
|
7
|
-
value('Pending', value: '
|
8
|
-
value('Configured', value: '
|
9
|
-
value('Active', value: '
|
10
|
-
value('Error', value: '
|
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
|
data/lib/osso/helpers/auth.rb
CHANGED
data/lib/osso/lib/route_map.rb
CHANGED
@@ -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
|
@@ -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
|
-
|
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: '
|
48
|
+
update(status: 'active')
|
49
49
|
end
|
50
50
|
|
51
51
|
def error!
|
52
|
-
update(status: '
|
52
|
+
update(status: 'error')
|
53
53
|
end
|
54
54
|
|
55
55
|
def root_url
|
@@ -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
|
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
@@ -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.
|
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
|
-
|
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
|
-
|
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/:
|
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,
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
data/lib/osso/routes/oauth.rb
CHANGED
@@ -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
|
-
|
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.not_pending
|
27
|
+
erb :multiple_providers if @providers.count > 1
|
33
28
|
|
34
|
-
|
35
|
-
|
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
|
data/lib/osso/version.rb
CHANGED
@@ -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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
43
|
+
:configured_identity_provider,
|
44
|
+
service: 'AZURE',
|
41
45
|
domain: enterprise.domain,
|
42
46
|
enterprise_account_id: enterprise.id,
|
43
47
|
)
|
data/spec/helpers/auth_spec.rb
CHANGED
@@ -8,13 +8,21 @@ describe Osso::Helpers::Auth do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
subject(:app) do
|
11
|
-
Class.new {
|
11
|
+
Class.new {
|
12
|
+
include Osso::Helpers::Auth
|
13
|
+
}
|
12
14
|
end
|
13
15
|
|
14
16
|
describe 'with the token as a header' do
|
15
17
|
before do
|
16
18
|
allow_any_instance_of(subject).to receive(:request) do
|
17
|
-
double('Request', env: { '
|
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
|
+
}
|
18
26
|
end
|
19
27
|
|
20
28
|
allow_any_instance_of(subject).to receive(:redirect) do
|
@@ -87,6 +95,170 @@ describe Osso::Helpers::Auth do
|
|
87
95
|
end
|
88
96
|
end
|
89
97
|
|
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
|
+
|
90
262
|
def encode(payload)
|
91
263
|
JWT.encode(
|
92
264
|
payload,
|
@@ -0,0 +1 @@
|
|
1
|
+
|
data/spec/routes/auth_spec.rb
CHANGED
@@ -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('
|
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('
|
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 %>
|
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.eta
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -272,6 +272,12 @@ files:
|
|
272
272
|
- lib/osso/db/migrate/20200723162228_drop_unneeded_tables.rb
|
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
|
+
- 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
|
275
281
|
- lib/osso/graphql/.DS_Store
|
276
282
|
- lib/osso/graphql/mutation.rb
|
277
283
|
- lib/osso/graphql/mutations.rb
|
@@ -312,6 +318,7 @@ files:
|
|
312
318
|
- lib/osso/lib/app_config.rb
|
313
319
|
- lib/osso/lib/oauth2_token.rb
|
314
320
|
- lib/osso/lib/route_map.rb
|
321
|
+
- lib/osso/lib/saml_handler.rb
|
315
322
|
- lib/osso/models/access_token.rb
|
316
323
|
- lib/osso/models/app_config.rb
|
317
324
|
- lib/osso/models/authorization_code.rb
|
@@ -326,8 +333,6 @@ files:
|
|
326
333
|
- lib/osso/routes/auth.rb
|
327
334
|
- lib/osso/routes/oauth.rb
|
328
335
|
- lib/osso/routes/routes.rb
|
329
|
-
- lib/osso/routes/views/error.erb
|
330
|
-
- lib/osso/routes/views/multiple_providers.erb
|
331
336
|
- lib/osso/version.rb
|
332
337
|
- lib/tasks/bootstrap.rake
|
333
338
|
- osso-rb.gemspec
|
@@ -348,6 +353,7 @@ files:
|
|
348
353
|
- spec/graphql/query/identity_provider_spec.rb
|
349
354
|
- spec/graphql/query/oauth_clients_spec.rb
|
350
355
|
- spec/helpers/auth_spec.rb
|
356
|
+
- spec/lib/saml_handler_spec.rb
|
351
357
|
- spec/models/identity_provider_spec.rb
|
352
358
|
- spec/routes/admin_spec.rb
|
353
359
|
- spec/routes/app_spec.rb
|
@@ -358,6 +364,7 @@ files:
|
|
358
364
|
- spec/support/spec_app.rb
|
359
365
|
- spec/support/views/admin.erb
|
360
366
|
- spec/support/views/error.erb
|
367
|
+
- spec/support/views/multiple_providers.erb
|
361
368
|
homepage: https://github.com/enterprise-oss/osso-rb
|
362
369
|
licenses:
|
363
370
|
- MIT
|
@@ -373,9 +380,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
373
380
|
version: 2.3.0
|
374
381
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
375
382
|
requirements:
|
376
|
-
- - "
|
383
|
+
- - ">"
|
377
384
|
- !ruby/object:Gem::Version
|
378
|
-
version:
|
385
|
+
version: 1.3.1
|
379
386
|
requirements: []
|
380
387
|
rubygems_version: 3.0.3
|
381
388
|
signing_key:
|
@@ -1 +0,0 @@
|
|
1
|
-
<%= @error %>
|
@@ -1 +0,0 @@
|
|
1
|
-
multiple providers
|