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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +23 -25
  3. data/LICENSE +21 -23
  4. data/db/schema.rb +2 -1
  5. data/lib/osso/db/migrate/20200929154117_add_users_count_to_identity_providers_and_enterprise_accounts.rb +6 -0
  6. data/lib/osso/error/account_configuration_error.rb +19 -0
  7. data/lib/osso/error/error.rb +6 -0
  8. data/lib/osso/error/missing_saml_attribute_error.rb +1 -1
  9. data/lib/osso/error/oauth_error.rb +24 -10
  10. data/lib/osso/error/saml_config_error.rb +5 -1
  11. data/lib/osso/graphql/mutation.rb +1 -0
  12. data/lib/osso/graphql/mutations.rb +1 -0
  13. data/lib/osso/graphql/mutations/delete_identity_provider.rb +24 -0
  14. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +1 -1
  15. data/lib/osso/graphql/types/enterprise_account.rb +1 -0
  16. data/lib/osso/graphql/types/identity_provider.rb +1 -0
  17. data/lib/osso/graphql/types/identity_provider_status.rb +4 -4
  18. data/lib/osso/helpers/auth.rb +1 -1
  19. data/lib/osso/lib/route_map.rb +1 -0
  20. data/lib/osso/models/enterprise_account.rb +1 -1
  21. data/lib/osso/models/identity_provider.rb +10 -6
  22. data/lib/osso/models/user.rb +2 -2
  23. data/lib/osso/routes/auth.rb +26 -8
  24. data/lib/osso/routes/oauth.rb +6 -5
  25. data/lib/osso/version.rb +1 -1
  26. data/spec/factories/enterprise_account.rb +8 -4
  27. data/spec/graphql/query/identity_provider_spec.rb +1 -0
  28. data/spec/helpers/auth_spec.rb +174 -2
  29. data/spec/models/identity_provider_spec.rb +13 -0
  30. data/spec/routes/auth_spec.rb +13 -9
  31. data/spec/routes/oauth_spec.rb +2 -1
  32. data/spec/support/views/error.erb +1 -0
  33. data/spec/support/views/multiple_providers.erb +1 -0
  34. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82bd613a1b8d5eb4f9c549d9d855c629698a120d2ee99bf3a52f6911fa4d080a
4
- data.tar.gz: a67aec622f76bcc42cc16065d11a795c09893caa1115759b5739fa384108f6f2
3
+ metadata.gz: f54c05ff3a991d49e4a47e036573d65f0bb2222371d73b10e665d02169d4a891
4
+ data.tar.gz: 24c4ea3adf0e1c599c315c48cb9700b7ae3d0e8b3c782c564c71692f5287e858
5
5
  SHA512:
6
- metadata.gz: c2a1ba5df34d2e175ed7a3ab7e61dcb573b5eb9a636753f7159860f817e6b61515b9350b63a1ac1ffece994f27a7f17d61d2de05e5f6a0468168cfb7a268f879
7
- data.tar.gz: 475eba77f824e32701ff9eae95bef4663f2e0de8d43c9d542846f6ecaec55edb1242251cb4ed50b9cb1db627252293cf406a633cc8c0c63fb7f419e10dc95e3c
6
+ metadata.gz: abd1565c7e9cdea79782176a8fde22e71a734c78fba6bde102066a5913eec4e97207295c1f1fe5dd1acfb2bc91bdb8842a72ec0cbea15a90faf3187e216a366d
7
+ data.tar.gz: 43e39d4a5738b59187a4ff9dc583292450815c0ceb6d4bfae29becc7ee0de9d8ddc4d34cd4b2c1e3d577a19690fbda81c5d0dff6f2aaf71527e3b9f9d9578dd8
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osso (0.0.5.pre.epsilon)
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.2)
22
- activesupport (= 6.0.3.2)
23
- activerecord (6.0.3.2)
24
- activemodel (= 6.0.3.2)
25
- activesupport (= 6.0.3.2)
26
- activesupport (6.0.3.2)
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.6)
43
- crack (0.4.3)
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.2)
50
+ factory_bot (6.1.0)
52
51
  activesupport (>= 5.0.0)
53
- faker (2.13.0)
52
+ faker (2.14.0)
54
53
  i18n (>= 1.6, < 2)
55
- graphql (1.11.4)
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.3)
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.1)
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.5)
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.7.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.86.0)
120
+ rubocop (0.91.0)
122
121
  parallel (~> 1.10)
123
- parser (>= 2.7.0.1)
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.3, < 1.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.1.0)
131
- parser (>= 2.7.0.1)
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.8.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.3.1)
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: Samuel Bauch
5
+ Licensor: EnterpriseOSS, Inc.
6
6
  Licensed Work: osso-rb
7
- The Licensed Work is (c) 2020 Samuel Bauch.
8
- Additional Use Grant: You may make use of the Licensed Work, provided that you do
9
- not use the Licensed Work in a Single Sign On Management
10
- Service.
11
-
12
- A "Single Sign On Management Service" is an offering
13
- (be it free or commercial) that uses the Licensed Work
14
- to allow third parties (other than your employees and
15
- contractors) to access the functionality of the
16
- Licensed Work such that any fourth parties directly
17
- benefit from the authentication, configuration, or
18
- documentation features of the Licensed Work.
19
-
20
- You thus may only use the Licensed Work in a manner
21
- whereby parties who directly benefit from the
22
- authentication, configuration, or documentation features
23
- of the Licensed Work are yourself, your employees or
24
- contractors, and your customers or partners.
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@enterprise-oss.dev
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.
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 2020_09_13_154919) do
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,6 @@
1
+ class AddUsersCountToIdentityProvidersAndEnterpriseAccounts < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :enterprise_accounts, :users_count, :integer, default: 0
4
+ add_column :identity_providers, :users_count, :integer, default: 0
5
+ end
6
+ 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
@@ -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,7 +2,7 @@
2
2
 
3
3
  module Osso
4
4
  module Error
5
- class MissingSamlAttributeError < StandardError; end
5
+ class MissingSamlAttributeError < Base; end
6
6
 
7
7
  class MissingSamlEmailAttributeError < MissingSamlAttributeError
8
8
  def message
@@ -2,27 +2,41 @@
2
2
 
3
3
  module Osso
4
4
  module Error
5
- class OAuthError < StandardError; end
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
- '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"
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 < MissingSamlAttributeError
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 check you're using the correct OAuth client identifier"
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 < MissingSamlAttributeError
31
+ class InvalidRedirectUri < OAuthError
32
+ def initialize(redirect_uri:)
33
+ @redirect_uri = redirect_uri
34
+ end
35
+
22
36
  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.'
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
@@ -2,7 +2,11 @@
2
2
 
3
3
  module Osso
4
4
  module Error
5
- class SamlConfigError < StandardError; end
5
+ class SamlConfigError < Base
6
+ def message
7
+ 'Something went wrong with your SAML configuration.'
8
+ end
9
+ end
6
10
 
7
11
  class InvalidACSURLError < SamlConfigError
8
12
  def message
@@ -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
@@ -14,6 +14,7 @@ module Osso
14
14
  field :domain, String, null: false
15
15
  field :identity_providers, [Types::IdentityProvider], null: true
16
16
  field :status, String, null: false
17
+ field :users_count, Integer, null: false
17
18
 
18
19
  def status
19
20
  'active'
@@ -16,6 +16,7 @@ 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 :acs_url_validator, String, null: false
19
20
  end
20
21
  end
21
22
  end
@@ -4,10 +4,10 @@ module Osso
4
4
  module GraphQL
5
5
  module Types
6
6
  class IdentityProviderStatus < BaseEnum
7
- value('Pending', value: 'PENDING')
8
- value('Configured', value: 'CONFIGURED')
9
- value('Active', value: 'ACTIVE')
10
- value('Error', value: 'ERROR')
7
+ value('Pending', value: 'pending')
8
+ value('Configured', value: 'configured')
9
+ value('Active', value: 'active')
10
+ value('Error', value: 'error')
11
11
  end
12
12
  end
13
13
  end
@@ -66,7 +66,7 @@ module Osso
66
66
  end
67
67
 
68
68
  def token
69
- request.env['admin_token'] || session['admin_token'] || request['admin_token']
69
+ session['admin_token'] || request.env['HTTP_AUTHORIZATION'] || request.params['admin_token']
70
70
  end
71
71
 
72
72
  def chomp_token
@@ -6,6 +6,7 @@ module Osso
6
6
  module RouteMap
7
7
  def self.included(klass)
8
8
  klass.class_eval do
9
+
9
10
  use Osso::Admin
10
11
  use Osso::Auth
11
12
  use Osso::Oauth
@@ -13,7 +13,7 @@ module Osso
13
13
  has_many :identity_providers
14
14
 
15
15
  def single_provider?
16
- identity_providers.one?
16
+ identity_providers.not_pending.one?
17
17
  end
18
18
 
19
19
  def provider
@@ -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 set_status
42
- return if status != 'PENDING'
43
+ def acs_url_validator
44
+ Regexp.escape(acs_url)
45
+ end
43
46
 
44
- self.status = 'CONFIGURED' if sso_url && sso_cert
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: 'ACTIVE')
52
+ update(status: 'active')
49
53
  end
50
54
 
51
55
  def error!
52
- update(status: 'ERROR')
56
+ update(status: 'error')
53
57
  end
54
58
 
55
59
  def root_url
@@ -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
 
@@ -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.find(identity_provider_id).
25
+ Models::IdentityProvider.
26
+ not_pending.
27
+ find(identity_provider_id).
26
28
  saml_options
29
+ rescue
30
+ {}
27
31
  end
28
32
  end
29
33
 
@@ -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::InvalidACSURLError => e
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
@@ -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
- rescue Osso::Error::OAuthError => e
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.5-epsilon'
4
+ VERSION = '0.0.5-lambda'
5
5
  end
@@ -11,7 +11,8 @@ FactoryBot.define do
11
11
  factory :enterprise_with_okta, parent: :enterprise_account do
12
12
  after :create do |enterprise|
13
13
  create(
14
- :okta_identity_provider,
14
+ :configured_identity_provider,
15
+ service: 'OKTA',
15
16
  domain: enterprise.domain,
16
17
  enterprise_account_id: enterprise.id,
17
18
  )
@@ -21,7 +22,8 @@ FactoryBot.define do
21
22
  factory :enterprise_with_azure, parent: :enterprise_account do
22
23
  after :create do |enterprise|
23
24
  create(
24
- :azure_identity_provider,
25
+ :configured_identity_provider,
26
+ service: 'AZURE',
25
27
  domain: enterprise.domain,
26
28
  enterprise_account_id: enterprise.id,
27
29
  )
@@ -31,13 +33,15 @@ FactoryBot.define do
31
33
  factory :enterprise_with_multiple_providers, parent: :enterprise_account do
32
34
  after :create do |enterprise|
33
35
  create(
34
- :okta_identity_provider,
36
+ :configured_identity_provider,
37
+ service: 'OKTA',
35
38
  domain: enterprise.domain,
36
39
  enterprise_account_id: enterprise.id,
37
40
  )
38
41
 
39
42
  create(
40
- :azure_identity_provider,
43
+ :configured_identity_provider,
44
+ service: 'AZURE',
41
45
  domain: enterprise.domain,
42
46
  enterprise_account_id: enterprise.id,
43
47
  )
@@ -15,6 +15,7 @@ describe Osso::GraphQL::Schema do
15
15
  service
16
16
  domain
17
17
  acsUrl
18
+ acsUrlValidator
18
19
  ssoCert
19
20
  ssoUrl
20
21
  status
@@ -8,13 +8,21 @@ describe Osso::Helpers::Auth do
8
8
  end
9
9
 
10
10
  subject(:app) do
11
- Class.new { include Osso::Helpers::Auth }
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: { 'admin_token' => token }, post?: false)
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).
@@ -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('ACTIVE')
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('ACTIVE')
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
- expect do
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
- end.to raise_error(Osso::Error::MissingSamlEmailAttributeError)
191
- end
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
- expect do
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
- end.to raise_error(Osso::Error::MissingSamlIdAttributeError)
207
+
208
+ expect(response.body).to eq('Osso::Error::MissingSamlIdAttributeError')
205
209
  end
206
210
  end
207
211
  end
@@ -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.epsilon
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-09-19 00:00:00.000000000 Z
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