osso 0.0.3.5 → 0.0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +4 -1
  3. data/Gemfile.lock +1 -1
  4. data/config/database.yml +2 -2
  5. data/db/schema.rb +133 -1
  6. data/lib/osso/db/migrate/20200328143305_create_identity_providers.rb +12 -0
  7. data/lib/osso/db/migrate/20200411184535_add_provider_id_to_users.rb +2 -2
  8. data/lib/osso/db/migrate/20200411192645_create_enterprise_accounts.rb +1 -1
  9. data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_accounts_and_identity_providers.rb +6 -0
  10. data/lib/osso/db/migrate/20200714223226_add_identity_provider_service_enum.rb +17 -0
  11. data/lib/osso/db/migrate/20200715154211_rename_idp_fields_on_identity_provider_to_sso.rb +6 -0
  12. data/lib/osso/db/migrate/20200715205801_add_name_to_enterprise_account.rb +5 -0
  13. data/lib/osso/graphql/mutation.rb +2 -1
  14. data/lib/osso/graphql/mutations.rb +2 -1
  15. data/lib/osso/graphql/mutations/base_mutation.rb +18 -7
  16. data/lib/osso/graphql/mutations/configure_identity_provider.rb +10 -13
  17. data/lib/osso/graphql/mutations/create_enterprise_account.rb +25 -0
  18. data/lib/osso/graphql/mutations/create_identity_provider.rb +9 -7
  19. data/lib/osso/graphql/mutations/{set_saml_provider.rb → set_identity_provider.rb} +4 -4
  20. data/lib/osso/graphql/query.rb +2 -2
  21. data/lib/osso/graphql/resolvers/oauth_clients.rb +1 -1
  22. data/lib/osso/graphql/schema.rb +1 -1
  23. data/lib/osso/graphql/types.rb +1 -0
  24. data/lib/osso/graphql/types/base_input_object.rb +10 -0
  25. data/lib/osso/graphql/types/enterprise_account.rb +1 -5
  26. data/lib/osso/graphql/types/identity_provider.rb +1 -13
  27. data/lib/osso/lib/app_config.rb +1 -1
  28. data/lib/osso/models/enterprise_account.rb +4 -4
  29. data/lib/osso/models/identity_provider.rb +48 -0
  30. data/lib/osso/models/models.rb +1 -1
  31. data/lib/osso/models/oauth_client.rb +1 -1
  32. data/lib/osso/models/saml_provider.rb +13 -16
  33. data/lib/osso/models/saml_providers/azure_saml_provider.rb +1 -1
  34. data/lib/osso/models/saml_providers/okta_saml_provider.rb +1 -1
  35. data/lib/osso/models/user.rb +3 -3
  36. data/lib/osso/routes/auth.rb +4 -4
  37. data/lib/osso/routes/oauth.rb +1 -1
  38. data/lib/osso/version.rb +1 -1
  39. data/spec/factories/enterprise_account.rb +5 -4
  40. data/spec/factories/identity_providers.rb +49 -0
  41. data/spec/factories/user.rb +1 -1
  42. data/spec/graphql/mutations/configure_identity_provider_spec.rb +65 -0
  43. data/spec/graphql/mutations/create_enterprise_account_spec.rb +68 -0
  44. data/spec/graphql/mutations/create_identity_provider_spec.rb +104 -0
  45. data/spec/graphql/query/enterprise_account_spec.rb +68 -0
  46. data/spec/graphql/query/enterprise_accounts_spec.rb +44 -0
  47. data/spec/graphql/query/identity_provider_spec.rb +62 -0
  48. data/spec/graphql/query/oauth_clients_account_spec.rb +48 -0
  49. data/spec/models/azure_saml_provider_spec.rb +14 -14
  50. data/spec/models/identity_provider_spec.rb +17 -0
  51. data/spec/models/okta_saml_provider_spec.rb +15 -15
  52. data/spec/routes/auth_spec.rb +9 -9
  53. data/spec/routes/oauth_spec.rb +1 -1
  54. data/spec/spec_helper.rb +1 -0
  55. metadata +20 -12
  56. data/lib/osso/db/migrate/20200411144528_create_saml_providers.rb +0 -13
  57. data/lib/osso/db/migrate/20200413153029_add_oauth_client_reference_to_saml_providers.rb +0 -5
  58. data/lib/osso/db/migrate/20200501203026_drop_null_constraints_from_saml_provider.rb +0 -7
  59. data/lib/osso/db/migrate/20200501204047_drop_acs_url.rb +0 -5
  60. data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_account.rb +0 -5
  61. data/lib/osso/db/migrate/20200601131227_drop_null_constraint_from_saml_providers_provider.rb +0 -7
  62. data/lib/osso/db/schema.rb +0 -132
  63. data/spec/factories/saml_providers.rb +0 -46
  64. data/spec/models/saml_provider_spec.rb +0 -31
@@ -31,7 +31,7 @@ module Osso
31
31
  case obj
32
32
  when Osso::Models::EnterpriseAccount
33
33
  Types::EnterpriseAccount
34
- when Osso::Models::SamlProvider
34
+ when Osso::Models::IdentityProvider
35
35
  Types::IdentityProvider
36
36
  else
37
37
  raise("Unexpected object: #{obj}")
@@ -7,6 +7,7 @@ end
7
7
 
8
8
  require_relative 'types/base_object'
9
9
  require_relative 'types/base_enum'
10
+ require_relative 'types/base_input_object'
10
11
  require_relative 'types/identity_provider_service'
11
12
  require_relative 'types/identity_provider'
12
13
  require_relative 'types/enterprise_account'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Types
6
+ class BaseInputObject < ::GraphQL::Schema::InputObject
7
+ end
8
+ end
9
+ end
10
+ end
@@ -16,16 +16,12 @@ module Osso
16
16
  field :identity_providers, [Types::IdentityProvider], null: true
17
17
  field :status, String, null: false
18
18
 
19
- def name
20
- object.domain.gsub('.com', '')
21
- end
22
-
23
19
  def status
24
20
  'active'
25
21
  end
26
22
 
27
23
  def identity_providers
28
- object.saml_providers
24
+ object.identity_providers
29
25
  end
30
26
  end
31
27
  end
@@ -19,20 +19,8 @@ module Osso
19
19
  field :sso_cert, String, null: true
20
20
  field :configured, Boolean, null: false
21
21
 
22
- def service
23
- @object.provider
24
- end
25
-
26
22
  def configured
27
- @object.idp_sso_target_url && @object.idp_cert
28
- end
29
-
30
- def sso_cert
31
- @object.idp_cert
32
- end
33
-
34
- def sso_url
35
- @object.idp_sso_target_url
23
+ !!(@object.sso_url && @object.sso_cert)
36
24
  end
37
25
  end
38
26
  end
@@ -7,7 +7,7 @@ module Osso
7
7
  def self.included(klass)
8
8
  klass.class_eval do
9
9
  use Rack::JSONBodyParser
10
- use Rack::Session::Cookie, secret: ENV.fetch('SESSION_SECRET')
10
+ use Rack::Session::Cookie, secret: ENV['SESSION_SECRET']
11
11
 
12
12
  error ActiveRecord::RecordNotFound do
13
13
  status 404
@@ -10,19 +10,19 @@ module Osso
10
10
  class EnterpriseAccount < ActiveRecord::Base
11
11
  belongs_to :oauth_client
12
12
  has_many :users
13
- has_many :saml_providers
13
+ has_many :identity_providers
14
14
 
15
15
  def single_provider?
16
- saml_providers.one?
16
+ identity_providers.one?
17
17
  end
18
18
 
19
19
  def provider
20
20
  return nil unless single_provider?
21
21
 
22
- saml_providers.first
22
+ identity_providers.first
23
23
  end
24
24
 
25
- alias saml_provider provider
25
+ alias identity_provider provider
26
26
  end
27
27
  end
28
28
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Models
5
+ # Base class for SAML Providers
6
+ class IdentityProvider < ActiveRecord::Base
7
+ NAME_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
8
+ belongs_to :enterprise_account
9
+ belongs_to :oauth_client
10
+ has_many :users
11
+
12
+ def name
13
+ service.titlecase
14
+ # raise(
15
+ # NoMethodError,
16
+ # '#name must be defined on each provider specific subclass',
17
+ # )
18
+ end
19
+
20
+ def saml_options
21
+ attributes.slice(
22
+ 'domain',
23
+ 'idp_cert',
24
+ 'idp_sso_target_url',
25
+ ).symbolize_keys
26
+ end
27
+
28
+ # def saml_options
29
+ # raise(
30
+ # NoMethodError,
31
+ # '#saml_options must be defined on each provider specific subclass',
32
+ # )
33
+ # end
34
+
35
+ def assertion_consumer_service_url
36
+ [
37
+ ENV.fetch('BASE_URL'),
38
+ 'auth',
39
+ 'saml',
40
+ id,
41
+ 'callback',
42
+ ].join('/')
43
+ end
44
+
45
+ alias acs_url assertion_consumer_service_url
46
+ end
47
+ end
48
+ end
@@ -12,5 +12,5 @@ require_relative 'authorization_code'
12
12
  require_relative 'enterprise_account'
13
13
  require_relative 'oauth_client'
14
14
  require_relative 'redirect_uri'
15
- require_relative 'saml_provider'
15
+ require_relative 'identity_provider'
16
16
  require_relative 'user'
@@ -6,7 +6,7 @@ module Osso
6
6
  class OauthClient < ActiveRecord::Base
7
7
  has_many :access_tokens
8
8
  has_many :refresh_tokens
9
- has_many :saml_providers
9
+ has_many :identity_providers
10
10
  has_many :redirect_uris
11
11
 
12
12
  before_validation :setup, on: :create
@@ -3,28 +3,27 @@
3
3
  module Osso
4
4
  module Models
5
5
  # Base class for SAML Providers
6
- class SamlProvider < ActiveRecord::Base
6
+ class IdentityProvider < ActiveRecord::Base
7
7
  NAME_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
8
- self.inheritance_column = :provider
9
8
  belongs_to :enterprise_account
10
9
  belongs_to :oauth_client
11
10
  has_many :users
12
11
 
13
12
  before_create :create_enterprise_account
14
13
 
15
- def name
16
- raise(
17
- NoMethodError,
18
- '#name must be defined on each provider specific subclass',
19
- )
20
- end
14
+ # def name
15
+ # raise(
16
+ # NoMethodError,
17
+ # '#name must be defined on each provider specific subclass',
18
+ # )
19
+ # end
21
20
 
22
- def saml_options
23
- raise(
24
- NoMethodError,
25
- '#saml_options must be defined on each provider specific subclass',
26
- )
27
- end
21
+ # def saml_options
22
+ # raise(
23
+ # NoMethodError,
24
+ # '#saml_options must be defined on each provider specific subclass',
25
+ # )
26
+ # end
28
27
 
29
28
  def assertion_consumer_service_url
30
29
  [
@@ -48,5 +47,3 @@ module Osso
48
47
  end
49
48
  end
50
49
  end
51
- require_relative 'saml_providers/azure_saml_provider'
52
- require_relative 'saml_providers/okta_saml_provider'
@@ -3,7 +3,7 @@
3
3
  module Osso
4
4
  module Models
5
5
  # Subclass for Azure / ADFS IDP instances
6
- class AzureSamlProvider < Models::SamlProvider
6
+ class AzureSamlProvider < Models::IdentityProvider
7
7
  def name
8
8
  'Azure'
9
9
  end
@@ -3,7 +3,7 @@
3
3
  module Osso
4
4
  module Models
5
5
  # Subclass for Okta IDP instances
6
- class OktaSamlProvider < Models::SamlProvider
6
+ class OktaSamlProvider < Models::IdentityProvider
7
7
  def name
8
8
  'Okta'
9
9
  end
@@ -4,19 +4,19 @@ module Osso
4
4
  module Models
5
5
  class User < ActiveRecord::Base
6
6
  belongs_to :enterprise_account
7
- belongs_to :saml_provider
7
+ belongs_to :identity_provider
8
8
  has_many :authorization_codes, dependent: :delete_all
9
9
  has_many :access_tokens, dependent: :delete_all
10
10
 
11
11
  def oauth_client
12
- saml_provider.oauth_client
12
+ identity_provider.oauth_client
13
13
  end
14
14
 
15
15
  def as_json(*)
16
16
  {
17
17
  email: email,
18
18
  id: id,
19
- idp: saml_provider.name,
19
+ idp: identity_provider.name,
20
20
  }
21
21
  end
22
22
  end
@@ -25,8 +25,8 @@ module Osso
25
25
  identity_provider_id_regex: UUID_REGEXP,
26
26
  path_prefix: '/saml',
27
27
  callback_suffix: 'callback',
28
- ) do |saml_provider_id, _env|
29
- provider = Models::SamlProvider.find(saml_provider_id)
28
+ ) do |identity_provider_id, _env|
29
+ provider = Models::IdentityProvider.find(identity_provider_id)
30
30
  provider.saml_options
31
31
  end
32
32
  end
@@ -38,7 +38,7 @@ module Osso
38
38
  # is redirected back to your application with this code
39
39
  # as a URL query param, which you then exhange for an access token
40
40
  post '/saml/:id/callback' do
41
- provider = Models::SamlProvider.find(params[:id])
41
+ provider = Models::IdentityProvider.find(params[:id])
42
42
  oauth_client = provider.oauth_client
43
43
  redirect_uri = env['redirect_uri'] || oauth_client.default_redirect_uri.uri
44
44
 
@@ -52,7 +52,7 @@ module Osso
52
52
  idp_id: attributes[:id],
53
53
  ).first_or_create! do |new_user|
54
54
  new_user.enterprise_account_id = provider.enterprise_account_id
55
- new_user.saml_provider_id = provider.id
55
+ new_user.identity_provider_id = provider.id
56
56
  end
57
57
 
58
58
  authorization_code = user.authorization_codes.create!(
@@ -14,7 +14,7 @@ module Osso
14
14
  # of the user who wants to sign in.
15
15
  get '/authorize' do
16
16
  @enterprise = Models::EnterpriseAccount.
17
- includes(:saml_providers).
17
+ includes(:identity_providers).
18
18
  find_by!(domain: params[:domain])
19
19
 
20
20
  Rack::OAuth2::Server::Authorize.new do |req, _res|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.3.5'
4
+ VERSION = '0.0.3.6'
5
5
  end
@@ -3,6 +3,7 @@
3
3
  FactoryBot.define do
4
4
  factory :enterprise_account, class: Osso::Models::EnterpriseAccount do
5
5
  id { SecureRandom.uuid }
6
+ name { Faker::Company.name }
6
7
  domain { Faker::Internet.domain_name }
7
8
  oauth_client
8
9
  end
@@ -10,7 +11,7 @@ FactoryBot.define do
10
11
  factory :enterprise_with_okta, parent: :enterprise_account do
11
12
  after :create do |enterprise|
12
13
  create(
13
- :okta_saml_provider,
14
+ :okta_identity_provider,
14
15
  domain: enterprise.domain,
15
16
  enterprise_account_id: enterprise.id,
16
17
  )
@@ -20,7 +21,7 @@ FactoryBot.define do
20
21
  factory :enterprise_with_azure, parent: :enterprise_account do
21
22
  after :create do |enterprise|
22
23
  create(
23
- :azure_saml_provider,
24
+ :azure_identity_provider,
24
25
  domain: enterprise.domain,
25
26
  enterprise_account_id: enterprise.id,
26
27
  )
@@ -30,13 +31,13 @@ FactoryBot.define do
30
31
  factory :enterprise_with_multiple_providers, parent: :enterprise_account do
31
32
  after :create do |enterprise|
32
33
  create(
33
- :okta_saml_provider,
34
+ :okta_identity_provider,
34
35
  domain: enterprise.domain,
35
36
  enterprise_account_id: enterprise.id,
36
37
  )
37
38
 
38
39
  create(
39
- :azure_saml_provider,
40
+ :azure_identity_provider,
40
41
  domain: enterprise.domain,
41
42
  enterprise_account_id: enterprise.id,
42
43
  )
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :identity_provider, class: Osso::Models::IdentityProvider do
5
+ id { SecureRandom.uuid }
6
+ domain { Faker::Internet.domain_name }
7
+ oauth_client
8
+
9
+ factory :okta_identity_provider, parent: :identity_provider do
10
+ service { 'OKTA' }
11
+ sso_url do
12
+ 'https://dev-162024.okta.com/app/vcardmedev162024_rubydemo2_1/exk51326b3U1941Hf4x6/sso/saml'
13
+ end
14
+ end
15
+
16
+ factory :azure_identity_provider, parent: :identity_provider do
17
+ service { 'AZURE' }
18
+ sso_url do
19
+ 'https://login.microsoftonline.com/0af6c610-c40c-4683-9ea4-f25e509b8172/saml2'
20
+ end
21
+ end
22
+
23
+ factory :configured_identity_provider, parent: :identity_provider do
24
+ sso_cert do
25
+ <<~CERT
26
+ -----BEGIN CERTIFICATE-----
27
+ MIIDpDCCAoygAwIBAgIGAXEiD4LlMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG
28
+ A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
29
+ MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xNjIwMjQxHDAaBgkqhkiG9w0BCQEW
30
+ DWluZm9Ab2t0YS5jb20wHhcNMjAwMzI4MTY1MTU0WhcNMzAwMzI4MTY1MjU0WjCBkjELMAkGA1UE
31
+ BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
32
+ BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTYyMDI0MRwwGgYJ
33
+ KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
34
+ wsnP4UTfv3bxR5Jh0at51Dqjj+fKxFznzFW3XA5NbF2SlRLjeYcvj3+47TC0eP6xOsLWfnvdnx4v
35
+ dd9Ufn7jDCo5pL3JykMVEh2I0szF3RLC+a532ArcwgU9Px48+rWVwPkASS7l4NHAM4+gOBHJMQt2
36
+ AMohPT0kU41P8BEPzfwhNyiEXR66JNZIJUE8fM3Vpgnxm/VSwYzJf0NfOyfxv8JczF0zkDbpE7Tk
37
+ 3Ww/PFFLoMxWzanWGJQ+blnhv6UV6H4fcfAbcwAplOdIVHjS2ghYBvYNGahuFxjia0+6csyZGrt8
38
+ H4XmR5Dr+jXY5K1b1VOA0k19/FCnHHN/smn25wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBgD9NE
39
+ 4OCuR1+vucV8S1T6XXIL2hB7bXBAZEVHZ1aErRzktgXAMgVwG267vIkD5VOXBiTy9yNU5LK6G3k2
40
+ zewU190sL1dMfyPnoVZyn94nvwe9A+on0tmZdmk00xirKk3FJdacnZNE9Dl/afIrcNf6xAm0WsU9
41
+ kbMiRwwvjO4TAiygDQzbrRC8ZfmT3hpBa3aTUzAccrvEQcgarLk4r7UjXP7a2mCN3UIIh+snN2Ms
42
+ vXHL0r6fM3xbniz+5lleWtPFw73yySBc8znkWZ4Tn8Lh0r6o5nCRYbr2REUB7ZIfiIyBbZxIp4kv
43
+ a+habbnQDFiNVzEd8OPXHh4EqLxOPDRW
44
+ -----END CERTIFICATE-----
45
+ CERT
46
+ end
47
+ end
48
+ end
49
+ end
@@ -5,7 +5,7 @@ FactoryBot.define do
5
5
  id { SecureRandom.uuid }
6
6
  email { Faker::Internet.email }
7
7
  idp_id { SecureRandom.hex(32) }
8
- saml_provider { create(:okta_saml_provider) }
8
+ identity_provider { create(:okta_identity_provider) }
9
9
  enterprise_account
10
10
  after(:create) do |user|
11
11
  create(
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Osso::GraphQL::Schema do
6
+ describe 'ConfigureIdentityProvider' do
7
+ let(:enterprise_account) { create(:enterprise_account) }
8
+ let(:identity_provider) { create(:identity_provider, enterprise_account: enterprise_account) }
9
+ let(:variables) do
10
+ {
11
+ input: {
12
+ id: identity_provider.id,
13
+ service: 'OKTA',
14
+ ssoUrl: 'https://example.com',
15
+ ssoCert: 'BEGIN_CERTIFICATE',
16
+ },
17
+ }
18
+ end
19
+ let(:mutation) do
20
+ <<~GRAPHQL
21
+ mutation ConfigureIdentityProvider($input: ConfigureIdentityProviderInput!) {
22
+ configureIdentityProvider(input: $input) {
23
+ identityProvider {
24
+ id
25
+ domain
26
+ configured
27
+ enterpriseAccountId
28
+ service
29
+ acsUrl
30
+ ssoCert
31
+ ssoUrl
32
+ }
33
+ }
34
+ }
35
+ GRAPHQL
36
+ end
37
+
38
+ subject do
39
+ described_class.execute(
40
+ mutation,
41
+ variables: variables,
42
+ context: { scope: current_scope },
43
+ )
44
+ end
45
+
46
+ describe 'for an admin user' do
47
+ let(:current_scope) { :admin }
48
+ it 'configures an identity provider' do
49
+ expect(subject.dig('data', 'configureIdentityProvider', 'identityProvider', 'configured')).
50
+ to be true
51
+ end
52
+ end
53
+
54
+ describe 'for an email scoped user' do
55
+ let(:domain) { Faker::Internet.domain_name }
56
+ let(:current_scope) { domain }
57
+ let(:enterprise_account) { create(:enterprise_account, domain: domain) }
58
+
59
+ it 'creates an identity provider' do
60
+ expect(subject.dig('data', 'configureIdentityProvider', 'identityProvider', 'domain')).
61
+ to eq(domain)
62
+ end
63
+ end
64
+ end
65
+ end