osso 0.0.5.pre.epsilon → 0.0.5.pre.lambda
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 +23 -25
- data/LICENSE +21 -23
- data/db/schema.rb +2 -1
- data/lib/osso/db/migrate/20200929154117_add_users_count_to_identity_providers_and_enterprise_accounts.rb +6 -0
- data/lib/osso/error/account_configuration_error.rb +19 -0
- data/lib/osso/error/error.rb +6 -0
- data/lib/osso/error/missing_saml_attribute_error.rb +1 -1
- data/lib/osso/error/oauth_error.rb +24 -10
- data/lib/osso/error/saml_config_error.rb +5 -1
- data/lib/osso/graphql/mutation.rb +1 -0
- data/lib/osso/graphql/mutations.rb +1 -0
- data/lib/osso/graphql/mutations/delete_identity_provider.rb +24 -0
- data/lib/osso/graphql/resolvers/enterprise_accounts.rb +1 -1
- data/lib/osso/graphql/types/enterprise_account.rb +1 -0
- data/lib/osso/graphql/types/identity_provider.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/models/enterprise_account.rb +1 -1
- data/lib/osso/models/identity_provider.rb +10 -6
- data/lib/osso/models/user.rb +2 -2
- data/lib/osso/routes/auth.rb +26 -8
- data/lib/osso/routes/oauth.rb +6 -5
- data/lib/osso/version.rb +1 -1
- data/spec/factories/enterprise_account.rb +8 -4
- data/spec/graphql/query/identity_provider_spec.rb +1 -0
- data/spec/helpers/auth_spec.rb +174 -2
- data/spec/models/identity_provider_spec.rb +13 -0
- data/spec/routes/auth_spec.rb +13 -9
- data/spec/routes/oauth_spec.rb +2 -1
- data/spec/support/views/error.erb +1 -0
- data/spec/support/views/multiple_providers.erb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f54c05ff3a991d49e4a47e036573d65f0bb2222371d73b10e665d02169d4a891
|
4
|
+
data.tar.gz: 24c4ea3adf0e1c599c315c48cb9700b7ae3d0e8b3c782c564c71692f5287e858
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abd1565c7e9cdea79782176a8fde22e71a734c78fba6bde102066a5913eec4e97207295c1f1fe5dd1acfb2bc91bdb8842a72ec0cbea15a90faf3187e216a366d
|
7
|
+
data.tar.gz: 43e39d4a5738b59187a4ff9dc583292450815c0ceb6d4bfae29becc7ee0de9d8ddc4d34cd4b2c1e3d577a19690fbda81c5d0dff6f2aaf71527e3b9f9d9578dd8
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
osso (0.0.5.pre.
|
4
|
+
osso (0.0.5.pre.lambda)
|
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
|
-
graphql (1.11.
|
54
|
+
graphql (1.11.5)
|
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/LICENSE
CHANGED
@@ -2,33 +2,31 @@ Business Source License 1.1
|
|
2
2
|
|
3
3
|
Parameters
|
4
4
|
|
5
|
-
Licensor:
|
5
|
+
Licensor: EnterpriseOSS, Inc.
|
6
6
|
Licensed Work: osso-rb
|
7
|
-
The Licensed Work is (c) 2020
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
Change Date: 2023-05-01
|
7
|
+
The Licensed Work is (c) 2020 EnterpriseOSS, Inc.
|
8
|
+
|
9
|
+
Additional Use Grant: You and your Authorized Users may make use of the
|
10
|
+
Licensed Work for your internal business purposes,
|
11
|
+
provided that you do not (i) rent, lease, copy, transfer,
|
12
|
+
resell, sublicense, lease, time-share, or otherwise provide
|
13
|
+
access to the Licensed Work to a third party (except
|
14
|
+
Authorized Users) or (ii) incorporate the Licensed Work
|
15
|
+
(or any portion of such) with, or use it with or to provide,
|
16
|
+
any site, product, or service, other than on sites/applications
|
17
|
+
owned and operated by you.
|
18
|
+
|
19
|
+
An “Authorized User” is defined as an individual person
|
20
|
+
(e.g. your employee, contractor, agent) who is registered and
|
21
|
+
permitted by you to use the Licensed Work subject to these
|
22
|
+
restrictions.
|
23
|
+
|
24
|
+
Change Date: 2025-10-01
|
27
25
|
|
28
26
|
Change License: Apache License, Version 2.0
|
29
27
|
|
30
28
|
For information about alternative licensing arrangements for the Software,
|
31
|
-
contact: hello@
|
29
|
+
contact: hello@enterpriseoss.dev
|
32
30
|
|
33
31
|
Notice
|
34
32
|
|
@@ -108,4 +106,4 @@ other recipients of the licensed work to be provided by Licensor:
|
|
108
106
|
|
109
107
|
3. To specify a Change Date.
|
110
108
|
|
111
|
-
4. Not to modify this License in any other way.
|
109
|
+
4. Not to modify this License in any other way.
|
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_29_154117) do
|
14
14
|
|
15
15
|
# These are extensions that must be enabled in order to support this database
|
16
16
|
enable_extension "pgcrypto"
|
@@ -57,6 +57,7 @@ ActiveRecord::Schema.define(version: 2020_09_13_154919) do
|
|
57
57
|
t.string "name", null: false
|
58
58
|
t.datetime "created_at", null: false
|
59
59
|
t.datetime "updated_at", null: false
|
60
|
+
t.integer "users_count", default: 0
|
60
61
|
t.index ["domain"], name: "index_enterprise_accounts_on_domain", unique: true
|
61
62
|
t.index ["oauth_client_id"], name: "index_enterprise_accounts_on_oauth_client_id"
|
62
63
|
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
|
data/lib/osso/error/error.rb
CHANGED
@@ -2,9 +2,15 @@
|
|
2
2
|
|
3
3
|
module Osso
|
4
4
|
module Error
|
5
|
+
class Base < StandardError
|
6
|
+
def docs_url
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
end
|
5
10
|
end
|
6
11
|
end
|
7
12
|
|
13
|
+
require_relative 'account_configuration_error'
|
8
14
|
require_relative 'missing_saml_attribute_error'
|
9
15
|
require_relative 'oauth_error'
|
10
16
|
require_relative 'saml_config_error'
|
@@ -2,27 +2,41 @@
|
|
2
2
|
|
3
3
|
module Osso
|
4
4
|
module Error
|
5
|
-
class OAuthError <
|
5
|
+
class OAuthError < Base
|
6
|
+
def docs_url
|
7
|
+
'https://ossoapp.com/docs/integration/oauth-consumption'
|
8
|
+
end
|
9
|
+
end
|
6
10
|
|
7
11
|
class NoAccountForOAuthClientError < OAuthError
|
12
|
+
def initialize(domain: 'the requested domain')
|
13
|
+
@domain = domain
|
14
|
+
end
|
15
|
+
|
8
16
|
def message
|
9
|
-
|
10
|
-
"Review our OAuth documentation, and check you're using the correct OAuth client identifier"
|
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.'
|
11
20
|
end
|
12
21
|
end
|
13
22
|
|
14
|
-
class InvalidOAuthClientIdentifier <
|
23
|
+
class InvalidOAuthClientIdentifier < OAuthError
|
15
24
|
def message
|
16
|
-
'No OAuth client exists for the requested OAuth client identifier.' \
|
17
|
-
"Review our OAuth documentation, and
|
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.'
|
18
28
|
end
|
19
29
|
end
|
20
30
|
|
21
|
-
class InvalidRedirectUri <
|
31
|
+
class InvalidRedirectUri < OAuthError
|
32
|
+
def initialize(redirect_uri:)
|
33
|
+
@redirect_uri = redirect_uri
|
34
|
+
end
|
35
|
+
|
22
36
|
def message
|
23
|
-
|
24
|
-
"Review our OAuth documentation, check you're using the correct OAuth client identifier " \
|
25
|
-
'and confirm your Redirect URI allow
|
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).'
|
26
40
|
end
|
27
41
|
end
|
28
42
|
end
|
@@ -11,6 +11,7 @@ module Osso
|
|
11
11
|
field :create_enterprise_account, mutation: Mutations::CreateEnterpriseAccount
|
12
12
|
field :create_oauth_client, mutation: Mutations::CreateOauthClient
|
13
13
|
field :delete_enterprise_account, mutation: Mutations::DeleteEnterpriseAccount
|
14
|
+
field :delete_identity_provider, mutation: Mutations::DeleteIdentityProvider
|
14
15
|
field :delete_oauth_client, mutation: Mutations::DeleteOauthClient
|
15
16
|
field :set_redirect_uris, mutation: Mutations::SetRedirectUris
|
16
17
|
field :regenerate_oauth_credentials, mutation: Mutations::RegenerateOauthCredentials
|
@@ -11,6 +11,7 @@ require_relative 'mutations/create_identity_provider'
|
|
11
11
|
require_relative 'mutations/create_enterprise_account'
|
12
12
|
require_relative 'mutations/create_oauth_client'
|
13
13
|
require_relative 'mutations/delete_enterprise_account'
|
14
|
+
require_relative 'mutations/delete_identity_provider'
|
14
15
|
require_relative 'mutations/delete_oauth_client'
|
15
16
|
require_relative 'mutations/regenerate_oauth_credentials'
|
16
17
|
require_relative 'mutations/set_redirect_uris'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Osso
|
4
|
+
module GraphQL
|
5
|
+
module Mutations
|
6
|
+
class DeleteIdentityProvider < BaseMutation
|
7
|
+
null false
|
8
|
+
|
9
|
+
argument :id, ID, required: true
|
10
|
+
|
11
|
+
field :identity_provider, Types::IdentityProvider, null: true
|
12
|
+
field :errors, [String], null: false
|
13
|
+
|
14
|
+
def resolve(id:)
|
15
|
+
identity_provider = Osso::Models::IdentityProvider.find(id)
|
16
|
+
|
17
|
+
return response_data(identity_provider: nil) if identity_provider.destroy
|
18
|
+
|
19
|
+
response_error(identity_provider.errors)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -11,7 +11,7 @@ module Osso
|
|
11
11
|
|
12
12
|
accounts = Osso::Models::EnterpriseAccount
|
13
13
|
|
14
|
-
accounts = accounts.order(sort_column => sort_order_sym(sort_order)) if sort_column && sort_order
|
14
|
+
accounts = accounts.order(sort_column.underscore => sort_order_sym(sort_order)) if sort_column && sort_order
|
15
15
|
|
16
16
|
accounts.all
|
17
17
|
end
|
@@ -4,10 +4,10 @@ module Osso
|
|
4
4
|
module GraphQL
|
5
5
|
module Types
|
6
6
|
class IdentityProviderStatus < BaseEnum
|
7
|
-
value('Pending', value: '
|
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
@@ -6,10 +6,12 @@ module Osso
|
|
6
6
|
class IdentityProvider < ActiveRecord::Base
|
7
7
|
belongs_to :enterprise_account
|
8
8
|
belongs_to :oauth_client
|
9
|
-
has_many :users
|
9
|
+
has_many :users, dependent: :delete_all
|
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
|
|
@@ -38,18 +40,20 @@ module Osso
|
|
38
40
|
|
39
41
|
alias acs_url assertion_consumer_service_url
|
40
42
|
|
41
|
-
def
|
42
|
-
|
43
|
+
def acs_url_validator
|
44
|
+
Regexp.escape(acs_url)
|
45
|
+
end
|
43
46
|
|
44
|
-
|
47
|
+
def set_status
|
48
|
+
self.status = 'configured' if sso_url && sso_cert && pending?
|
45
49
|
end
|
46
50
|
|
47
51
|
def active!
|
48
|
-
update(status: '
|
52
|
+
update(status: 'active')
|
49
53
|
end
|
50
54
|
|
51
55
|
def error!
|
52
|
-
update(status: '
|
56
|
+
update(status: 'error')
|
53
57
|
end
|
54
58
|
|
55
59
|
def root_url
|
data/lib/osso/models/user.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Osso
|
4
4
|
module Models
|
5
5
|
class User < ActiveRecord::Base
|
6
|
-
belongs_to :enterprise_account
|
7
|
-
belongs_to :identity_provider
|
6
|
+
belongs_to :enterprise_account, counter_cache: true
|
7
|
+
belongs_to :identity_provider, counter_cache: true
|
8
8
|
has_many :authorization_codes, dependent: :delete_all
|
9
9
|
has_many :access_tokens, dependent: :delete_all
|
10
10
|
|
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,8 +22,12 @@ 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
|
|
@@ -31,12 +35,26 @@ module Osso
|
|
31
35
|
OmniAuth::FailureEndpoint.new(env).redirect_to_failure
|
32
36
|
end
|
33
37
|
|
34
|
-
error do
|
35
|
-
erb :error
|
36
|
-
end
|
37
|
-
|
38
38
|
namespace '/auth' do
|
39
39
|
get '/failure' do
|
40
|
+
# ??? invalid ticket, warden throws, ugh
|
41
|
+
|
42
|
+
# confirmed:
|
43
|
+
# - a valid but wrong cert will throw here
|
44
|
+
# (OneLogin::RubySaml::ValidationError, Fingerprint mismatch)
|
45
|
+
# but an _invalid_ cert is not caught. we do validate certs on
|
46
|
+
# configuration, so this may be ok
|
47
|
+
#
|
48
|
+
# - a valid but wrong ACS URL will throw here. the urls
|
49
|
+
# are pretty complex, but it has come up
|
50
|
+
#
|
51
|
+
# - specifying the wrong "recipient" in your IDP. Only OL so far
|
52
|
+
# (OneLogin::RubySaml::ValidationError, The response was received
|
53
|
+
# at vcardme.com instead of
|
54
|
+
# http://localhost:9292/auth/saml/e54a9a92-b4b5-4ea5-b0e3-b1423eb20b76/callback)
|
55
|
+
|
56
|
+
|
57
|
+
@error = Osso::Error::SamlConfigError.new
|
40
58
|
erb :error
|
41
59
|
end
|
42
60
|
# Enterprise users are sent here after authenticating against
|
@@ -52,10 +70,10 @@ module Osso
|
|
52
70
|
)
|
53
71
|
|
54
72
|
redirect(redirect_uri)
|
55
|
-
rescue Osso::Error::
|
73
|
+
rescue Osso::Error::Base => e
|
56
74
|
@error = e
|
57
75
|
erb :error
|
58
|
-
end
|
76
|
+
end
|
59
77
|
end
|
60
78
|
end
|
61
79
|
end
|
data/lib/osso/routes/oauth.rb
CHANGED
@@ -23,10 +23,11 @@ module Osso
|
|
23
23
|
|
24
24
|
redirect "/auth/saml/#{enterprise.provider.id}" if enterprise.single_provider?
|
25
25
|
|
26
|
-
@providers = enterprise.identity_providers
|
27
|
-
erb :multiple_providers
|
26
|
+
@providers = enterprise.identity_providers.not_pending
|
27
|
+
return erb :multiple_providers if @providers.count > 1
|
28
28
|
|
29
|
-
|
29
|
+
raise Osso::Error::MissingConfiguredIdentityProvider.new(domain: params[:domain])
|
30
|
+
rescue Osso::Error::Base => e
|
30
31
|
@error = e
|
31
32
|
erb :error
|
32
33
|
end
|
@@ -64,7 +65,7 @@ module Osso
|
|
64
65
|
includes(:identity_providers).
|
65
66
|
find_by!(domain: domain, oauth_client_id: client_id)
|
66
67
|
rescue ActiveRecord::RecordNotFound
|
67
|
-
raise Osso::Error::NoAccountForOAuthClientError
|
68
|
+
raise Osso::Error::NoAccountForOAuthClientError.new(domain: params[:domain])
|
68
69
|
end
|
69
70
|
|
70
71
|
def find_client(identifier)
|
@@ -80,7 +81,7 @@ module Osso
|
|
80
81
|
session[:osso_oauth_state] = params[:state]
|
81
82
|
end.call(env)
|
82
83
|
rescue Rack::OAuth2::Server::Authorize::BadRequest
|
83
|
-
raise Osso::Error::InvalidRedirectUri
|
84
|
+
raise Osso::Error::InvalidRedirectUri.new(redirect_uri: params[:redirect_uri])
|
84
85
|
end
|
85
86
|
end
|
86
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,
|
@@ -24,6 +24,19 @@ describe Osso::Models::IdentityProvider do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
describe '#acs_url_validator' do
|
28
|
+
it 'returns a regex escaped string' do
|
29
|
+
allow(subject).to receive(:acs_url).and_return(
|
30
|
+
'https://foo.com/auth/saml/callback'
|
31
|
+
)
|
32
|
+
|
33
|
+
expect(subject.acs_url_validator).to eq(
|
34
|
+
'https://foo\\.com/auth/saml/callback'
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
27
40
|
describe '#saml_options' do
|
28
41
|
it 'returns the required args' do
|
29
42
|
expect(subject.saml_options).
|
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) }
|
@@ -108,7 +111,7 @@ describe Osso::Auth do
|
|
108
111
|
'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
|
109
112
|
},
|
110
113
|
)
|
111
|
-
expect(okta_provider.reload.status).to eq('
|
114
|
+
expect(okta_provider.reload.status).to eq('active')
|
112
115
|
end
|
113
116
|
end
|
114
117
|
end
|
@@ -143,7 +146,7 @@ describe Osso::Auth do
|
|
143
146
|
},
|
144
147
|
)
|
145
148
|
|
146
|
-
expect(azure_provider.reload.status).to eq('
|
149
|
+
expect(azure_provider.reload.status).to eq('active')
|
147
150
|
end
|
148
151
|
end
|
149
152
|
|
@@ -179,29 +182,30 @@ describe Osso::Auth do
|
|
179
182
|
it 'raises an error when email is missing' do
|
180
183
|
mock_saml_omniauth(email: nil, id: SecureRandom.uuid)
|
181
184
|
|
182
|
-
|
183
|
-
post(
|
185
|
+
|
186
|
+
response = post(
|
184
187
|
"/auth/saml/#{azure_provider.id}/callback",
|
185
188
|
nil,
|
186
189
|
{
|
187
190
|
'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
|
188
191
|
},
|
189
192
|
)
|
190
|
-
|
191
|
-
|
193
|
+
|
194
|
+
expect(response.body).to eq('Osso::Error::MissingSamlEmailAttributeError')
|
195
|
+
end
|
192
196
|
|
193
197
|
it 'raises an error when id is missing' do
|
194
198
|
mock_saml_omniauth(email: Faker::Internet.email, id: nil)
|
195
199
|
|
196
|
-
|
197
|
-
post(
|
200
|
+
response = post(
|
198
201
|
"/auth/saml/#{azure_provider.id}/callback",
|
199
202
|
nil,
|
200
203
|
{
|
201
204
|
'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
|
202
205
|
},
|
203
206
|
)
|
204
|
-
|
207
|
+
|
208
|
+
expect(response.body).to eq('Osso::Error::MissingSamlIdAttributeError')
|
205
209
|
end
|
206
210
|
end
|
207
211
|
end
|
data/spec/routes/oauth_spec.rb
CHANGED
@@ -48,7 +48,7 @@ describe Osso::Oauth do
|
|
48
48
|
|
49
49
|
describe 'for an enterprise domain with multiple SAML providers' do
|
50
50
|
it 'renders the multiple providers screen' do
|
51
|
-
enterprise = create(:enterprise_with_multiple_providers)
|
51
|
+
enterprise = create(:enterprise_with_multiple_providers, oauth_client: client)
|
52
52
|
|
53
53
|
get(
|
54
54
|
'/oauth/authorize',
|
@@ -59,6 +59,7 @@ describe Osso::Oauth do
|
|
59
59
|
)
|
60
60
|
|
61
61
|
expect(last_response).to be_ok
|
62
|
+
expect(last_response.body).to eq("MULITPLE PROVIDERS")
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @error %>
|
@@ -0,0 +1 @@
|
|
1
|
+
MULITPLE PROVIDERS
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: osso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.5.pre.
|
4
|
+
version: 0.0.5.pre.lambda
|
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-
|
11
|
+
date: 2020-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -273,6 +273,8 @@ files:
|
|
273
273
|
- lib/osso/db/migrate/20200826201852_create_app_config.rb
|
274
274
|
- lib/osso/db/migrate/20200913154919_add_one_login_to_identity_provider_service_enum.rb
|
275
275
|
- lib/osso/db/migrate/20200916125543_add_google_to_identity_provider_service_enum.rb
|
276
|
+
- lib/osso/db/migrate/20200929154117_add_users_count_to_identity_providers_and_enterprise_accounts.rb
|
277
|
+
- lib/osso/error/account_configuration_error.rb
|
276
278
|
- lib/osso/error/error.rb
|
277
279
|
- lib/osso/error/missing_saml_attribute_error.rb
|
278
280
|
- lib/osso/error/oauth_error.rb
|
@@ -286,6 +288,7 @@ files:
|
|
286
288
|
- lib/osso/graphql/mutations/create_identity_provider.rb
|
287
289
|
- lib/osso/graphql/mutations/create_oauth_client.rb
|
288
290
|
- lib/osso/graphql/mutations/delete_enterprise_account.rb
|
291
|
+
- lib/osso/graphql/mutations/delete_identity_provider.rb
|
289
292
|
- lib/osso/graphql/mutations/delete_oauth_client.rb
|
290
293
|
- lib/osso/graphql/mutations/regenerate_oauth_credentials.rb
|
291
294
|
- lib/osso/graphql/mutations/set_redirect_uris.rb
|