osso 0.0.3.6 → 0.0.3.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +5 -3
  3. data/Gemfile.lock +5 -1
  4. data/bin/annotate +1 -0
  5. data/db/schema.rb +11 -54
  6. data/lib/osso/db/migrate/20200714223226_add_identity_provider_service_enum.rb +1 -1
  7. data/lib/osso/db/migrate/20200722230116_add_identity_provider_status_enum_and_use_on_identity_providers.rb +15 -0
  8. data/lib/osso/db/migrate/20200723153750_add_missing_timestamps.rb +35 -0
  9. data/lib/osso/db/migrate/20200723162228_drop_unneeded_tables.rb +9 -0
  10. data/lib/osso/graphql/mutation.rb +4 -2
  11. data/lib/osso/graphql/mutations.rb +3 -1
  12. data/lib/osso/graphql/mutations/base_mutation.rb +8 -2
  13. data/lib/osso/graphql/mutations/configure_identity_provider.rb +10 -1
  14. data/lib/osso/graphql/mutations/create_oauth_client.rb +30 -0
  15. data/lib/osso/graphql/mutations/delete_enterprise_account.rb +34 -0
  16. data/lib/osso/graphql/mutations/delete_oauth_client.rb +30 -0
  17. data/lib/osso/graphql/query.rb +4 -1
  18. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +12 -4
  19. data/lib/osso/graphql/resolvers/oauth_clients.rb +1 -1
  20. data/lib/osso/graphql/schema.rb +4 -0
  21. data/lib/osso/graphql/types.rb +2 -0
  22. data/lib/osso/graphql/types/base_connection.rb +15 -0
  23. data/lib/osso/graphql/types/base_object.rb +4 -0
  24. data/lib/osso/graphql/types/enterprise_account.rb +4 -0
  25. data/lib/osso/graphql/types/identity_provider.rb +8 -3
  26. data/lib/osso/graphql/types/identity_provider_status.rb +14 -0
  27. data/lib/osso/graphql/types/oauth_client.rb +13 -1
  28. data/lib/osso/helpers/auth.rb +11 -12
  29. data/lib/osso/models/access_token.rb +18 -0
  30. data/lib/osso/models/authorization_code.rb +20 -0
  31. data/lib/osso/models/enterprise_account.rb +20 -0
  32. data/lib/osso/models/identity_provider.rb +29 -0
  33. data/lib/osso/models/models.rb +2 -0
  34. data/lib/osso/models/oauth_client.rb +17 -1
  35. data/lib/osso/models/redirect_uri.rb +17 -0
  36. data/lib/osso/models/user.rb +22 -0
  37. data/lib/osso/version.rb +1 -1
  38. data/osso-rb.gemspec +1 -0
  39. data/spec/factories/identity_providers.rb +22 -0
  40. data/spec/graphql/mutations/configure_identity_provider_spec.rb +14 -4
  41. data/spec/graphql/mutations/create_oauth_client_spec.rb +55 -0
  42. data/spec/graphql/mutations/delete_enterprise_account_spec.rb +63 -0
  43. data/spec/graphql/mutations/delete_oauth_client_spec.rb +51 -0
  44. data/spec/graphql/query/enterprise_account_spec.rb +1 -1
  45. data/spec/graphql/query/enterprise_accounts_spec.rb +32 -18
  46. data/spec/graphql/query/identity_provider_spec.rb +9 -6
  47. data/spec/graphql/query/{oauth_clients_account_spec.rb → oauth_clients_spec.rb} +2 -0
  48. metadata +30 -8
  49. data/lib/osso/db/migrate/20200328143303_create_oauth_tables.rb +0 -57
  50. data/lib/osso/graphql/mutations/set_identity_provider.rb +0 -27
  51. data/lib/osso/models/saml_provider.rb +0 -49
  52. data/lib/osso/models/saml_providers/azure_saml_provider.rb +0 -22
  53. data/lib/osso/models/saml_providers/okta_saml_provider.rb +0 -23
@@ -37,6 +37,10 @@ module Osso
37
37
  raise("Unexpected object: #{obj}")
38
38
  end
39
39
  end
40
+
41
+ def self.unauthorized_object(error)
42
+ raise ::GraphQL::ExecutionError, "An object of type #{error.type.graphql_name} was hidden due to permissions"
43
+ end
40
44
  end
41
45
  end
42
46
  end
@@ -5,10 +5,12 @@ module Osso
5
5
  end
6
6
  end
7
7
 
8
+ require_relative 'types/base_connection'
8
9
  require_relative 'types/base_object'
9
10
  require_relative 'types/base_enum'
10
11
  require_relative 'types/base_input_object'
11
12
  require_relative 'types/identity_provider_service'
13
+ require_relative 'types/identity_provider_status'
12
14
  require_relative 'types/identity_provider'
13
15
  require_relative 'types/enterprise_account'
14
16
  require_relative 'types/oauth_client'
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Types
6
+ class BaseConnection < ::GraphQL::Types::Relay::BaseConnection
7
+ field :total_count, Integer, null: false
8
+
9
+ def total_count
10
+ object.items&.count
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -6,6 +6,10 @@ module Osso
6
6
  module GraphQL
7
7
  module Types
8
8
  class BaseObject < ::GraphQL::Schema::Object
9
+ connection_type_class GraphQL::Types::BaseConnection
10
+
11
+ field :created_at, ::GraphQL::Types::ISO8601DateTime, null: false
12
+ field :updated_at, ::GraphQL::Types::ISO8601DateTime, null: false
9
13
  end
10
14
  end
11
15
  end
@@ -23,6 +23,10 @@ module Osso
23
23
  def identity_providers
24
24
  object.identity_providers
25
25
  end
26
+
27
+ def self.authorized?(object, context)
28
+ super && (context[:scope] == :admin || object.domain == context[:scope])
29
+ end
26
30
  end
27
31
  end
28
32
  end
@@ -17,10 +17,15 @@ module Osso
17
17
  field :acs_url, String, null: false
18
18
  field :sso_url, String, null: true
19
19
  field :sso_cert, String, null: true
20
- field :configured, Boolean, null: false
20
+ field :status, Types::IdentityProviderStatus, null: false
21
+ field :documentation_pdf_url, String, null: true
21
22
 
22
- def configured
23
- !!(@object.sso_url && @object.sso_cert)
23
+ def documentation_pdf_url
24
+ ENV['BASE_URL'] + '/identity_provider/documentation/' + @object.id
25
+ end
26
+
27
+ def self.authorized?(object, context)
28
+ super && (context[:scope] == :admin || object.domain == context[:scope])
24
29
  end
25
30
  end
26
31
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Types
6
+ class IdentityProviderStatus < BaseEnum
7
+ value('Pending', value: 'PENDING')
8
+ value('Configured', value: 'CONFIGURED')
9
+ value('Active', value: 'ACTIVE')
10
+ value('Error', value: 'ERROR')
11
+ end
12
+ end
13
+ end
14
+ end
@@ -5,7 +5,7 @@ require 'graphql'
5
5
  module Osso
6
6
  module GraphQL
7
7
  module Types
8
- class OAuthClient < Types::BaseObject
8
+ class OauthClient < Types::BaseObject
9
9
  description 'An OAuth client used to consume Osso SAML users'
10
10
  implements ::GraphQL::Types::Relay::Node
11
11
 
@@ -14,6 +14,18 @@ module Osso
14
14
  field :name, String, null: false
15
15
  field :client_id, String, null: false
16
16
  field :client_secret, String, null: false
17
+
18
+ def client_id
19
+ object.identifier
20
+ end
21
+
22
+ def client_secret
23
+ object.secret
24
+ end
25
+
26
+ def self.authorized?(object, context)
27
+ super && context[:scope] == :admin
28
+ end
17
29
  end
18
30
  end
19
31
  end
@@ -15,12 +15,7 @@ module Osso
15
15
  end
16
16
 
17
17
  def enterprise_authorized?(_domain)
18
- payload, _args = JWT.decode(
19
- token,
20
- ENV['JWT_HMAC_SECRET'],
21
- true,
22
- { algorithm: 'HS256' },
23
- )
18
+ payload, _args = decode(token)
24
19
 
25
20
  @current_scope = payload['scope']
26
21
 
@@ -36,12 +31,7 @@ module Osso
36
31
  end
37
32
 
38
33
  def admin_authorized?
39
- payload, _args = JWT.decode(
40
- token,
41
- ENV['JWT_HMAC_SECRET'],
42
- true,
43
- { algorithm: 'HS256' },
44
- )
34
+ payload, _args = decode(token)
45
35
 
46
36
  if payload['scope'] == 'admin'
47
37
  @current_scope = :admin
@@ -66,6 +56,15 @@ module Osso
66
56
 
67
57
  redirect request.path
68
58
  end
59
+
60
+ def decode(token)
61
+ JWT.decode(
62
+ token,
63
+ ENV['JWT_HMAC_SECRET'],
64
+ true,
65
+ { algorithm: 'HS256' },
66
+ )
67
+ end
69
68
  end
70
69
  end
71
70
  end
@@ -27,3 +27,21 @@ module Osso
27
27
  end
28
28
  end
29
29
  end
30
+
31
+ # == Schema Information
32
+ #
33
+ # Table name: access_tokens
34
+ #
35
+ # id :uuid not null, primary key
36
+ # token :string
37
+ # expires_at :datetime
38
+ # created_at :datetime not null
39
+ # updated_at :datetime not null
40
+ # user_id :uuid
41
+ # oauth_client_id :uuid
42
+ #
43
+ # Indexes
44
+ #
45
+ # index_access_tokens_on_oauth_client_id (oauth_client_id)
46
+ # index_access_tokens_on_user_id (user_id)
47
+ #
@@ -12,3 +12,23 @@ module Osso
12
12
  end
13
13
  end
14
14
  end
15
+
16
+ # == Schema Information
17
+ #
18
+ # Table name: authorization_codes
19
+ #
20
+ # id :uuid not null, primary key
21
+ # token :string
22
+ # redirect_uri :string
23
+ # expires_at :datetime
24
+ # created_at :datetime not null
25
+ # updated_at :datetime not null
26
+ # user_id :uuid
27
+ # oauth_client_id :uuid
28
+ #
29
+ # Indexes
30
+ #
31
+ # index_authorization_codes_on_oauth_client_id (oauth_client_id)
32
+ # index_authorization_codes_on_token (token) UNIQUE
33
+ # index_authorization_codes_on_user_id (user_id)
34
+ #
@@ -26,3 +26,23 @@ module Osso
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ # == Schema Information
31
+ #
32
+ # Table name: enterprise_accounts
33
+ #
34
+ # id :uuid not null, primary key
35
+ # domain :string not null
36
+ # external_uuid :uuid
37
+ # external_int_id :integer
38
+ # external_id :string
39
+ # oauth_client_id :uuid
40
+ # name :string not null
41
+ # created_at :datetime not null
42
+ # updated_at :datetime not null
43
+ #
44
+ # Indexes
45
+ #
46
+ # index_enterprise_accounts_on_domain (domain) UNIQUE
47
+ # index_enterprise_accounts_on_oauth_client_id (oauth_client_id)
48
+ #
@@ -8,6 +8,7 @@ module Osso
8
8
  belongs_to :enterprise_account
9
9
  belongs_to :oauth_client
10
10
  has_many :users
11
+ before_save :set_status
11
12
 
12
13
  def name
13
14
  service.titlecase
@@ -43,6 +44,34 @@ module Osso
43
44
  end
44
45
 
45
46
  alias acs_url assertion_consumer_service_url
47
+
48
+ def set_status
49
+ return if status != 'PENDING'
50
+
51
+ self.status = 'CONFIGURED' if sso_url && sso_cert
52
+ end
46
53
  end
47
54
  end
48
55
  end
56
+
57
+ # == Schema Information
58
+ #
59
+ # Table name: identity_providers
60
+ #
61
+ # id :uuid not null, primary key
62
+ # service :string
63
+ # domain :string not null
64
+ # sso_url :string
65
+ # sso_cert :text
66
+ # enterprise_account_id :uuid
67
+ # oauth_client_id :uuid
68
+ # status :enum default("PENDING")
69
+ # created_at :datetime
70
+ # updated_at :datetime
71
+ #
72
+ # Indexes
73
+ #
74
+ # index_identity_providers_on_domain (domain)
75
+ # index_identity_providers_on_enterprise_account_id (enterprise_account_id)
76
+ # index_identity_providers_on_oauth_client_id (oauth_client_id)
77
+ #
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # -*- SkipSchemaAnnotations
4
+
3
5
  require 'sinatra/activerecord'
4
6
 
5
7
  module Osso
@@ -25,8 +25,24 @@ module Osso
25
25
 
26
26
  def setup
27
27
  self.identifier = SecureRandom.hex(16)
28
- self.secret = SecureRandom.hex(64)
28
+ self.secret = SecureRandom.hex(32)
29
29
  end
30
30
  end
31
31
  end
32
32
  end
33
+
34
+ # == Schema Information
35
+ #
36
+ # Table name: oauth_clients
37
+ #
38
+ # id :uuid not null, primary key
39
+ # name :string not null
40
+ # secret :string not null
41
+ # identifier :string not null
42
+ # created_at :datetime not null
43
+ # updated_at :datetime not null
44
+ #
45
+ # Indexes
46
+ #
47
+ # index_oauth_clients_on_identifier (identifier) UNIQUE
48
+ #
@@ -18,3 +18,20 @@ module Osso
18
18
  end
19
19
  end
20
20
  end
21
+
22
+ # == Schema Information
23
+ #
24
+ # Table name: redirect_uris
25
+ #
26
+ # id :uuid not null, primary key
27
+ # uri :string not null
28
+ # primary :boolean default(FALSE), not null
29
+ # oauth_client_id :uuid
30
+ # created_at :datetime not null
31
+ # updated_at :datetime not null
32
+ #
33
+ # Indexes
34
+ #
35
+ # index_redirect_uris_on_oauth_client_id (oauth_client_id)
36
+ # index_redirect_uris_on_uri_and_primary (uri,primary) UNIQUE
37
+ #
@@ -22,3 +22,25 @@ module Osso
22
22
  end
23
23
  end
24
24
  end
25
+
26
+ # == Schema Information
27
+ #
28
+ # Table name: users
29
+ #
30
+ # id :uuid not null, primary key
31
+ # email :string not null
32
+ # idp_id :string not null
33
+ # identity_provider_id :uuid
34
+ # enterprise_account_id :uuid
35
+ # created_at :datetime not null
36
+ # updated_at :datetime not null
37
+ #
38
+ # Indexes
39
+ #
40
+ # index_users_on_email_and_idp_id (email,idp_id) UNIQUE
41
+ # index_users_on_enterprise_account_id (enterprise_account_id)
42
+ #
43
+ # Foreign Keys
44
+ #
45
+ # fk_rails_... (identity_provider_id => identity_providers.id)
46
+ #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.3.6'
4
+ VERSION = '0.0.3.11'
5
5
  end
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_runtime_dependency 'sinatra-activerecord'
29
29
  spec.add_runtime_dependency 'sinatra-contrib'
30
30
 
31
+ spec.add_development_dependency 'annotate', '~> 3.1'
31
32
  spec.add_development_dependency 'bundler', '~> 2.1'
32
33
  spec.add_development_dependency 'pry'
33
34
 
@@ -47,3 +47,25 @@ FactoryBot.define do
47
47
  end
48
48
  end
49
49
  end
50
+
51
+ # == Schema Information
52
+ #
53
+ # Table name: identity_providers
54
+ #
55
+ # id :uuid not null, primary key
56
+ # service :string
57
+ # domain :string not null
58
+ # sso_url :string
59
+ # sso_cert :text
60
+ # enterprise_account_id :uuid
61
+ # oauth_client_id :uuid
62
+ # status :enum default("PENDING")
63
+ # created_at :datetime
64
+ # updated_at :datetime
65
+ #
66
+ # Indexes
67
+ #
68
+ # index_identity_providers_on_domain (domain)
69
+ # index_identity_providers_on_enterprise_account_id (enterprise_account_id)
70
+ # index_identity_providers_on_oauth_client_id (oauth_client_id)
71
+ #
@@ -23,7 +23,7 @@ describe Osso::GraphQL::Schema do
23
23
  identityProvider {
24
24
  id
25
25
  domain
26
- configured
26
+ status
27
27
  enterpriseAccountId
28
28
  service
29
29
  acsUrl
@@ -46,8 +46,8 @@ describe Osso::GraphQL::Schema do
46
46
  describe 'for an admin user' do
47
47
  let(:current_scope) { :admin }
48
48
  it 'configures an identity provider' do
49
- expect(subject.dig('data', 'configureIdentityProvider', 'identityProvider', 'configured')).
50
- to be true
49
+ expect(subject.dig('data', 'configureIdentityProvider', 'identityProvider', 'status')).
50
+ to eq('Configured')
51
51
  end
52
52
  end
53
53
 
@@ -55,11 +55,21 @@ describe Osso::GraphQL::Schema do
55
55
  let(:domain) { Faker::Internet.domain_name }
56
56
  let(:current_scope) { domain }
57
57
  let(:enterprise_account) { create(:enterprise_account, domain: domain) }
58
+ let(:identity_provider) { create(:identity_provider, enterprise_account: enterprise_account, domain: domain) }
58
59
 
59
- it 'creates an identity provider' do
60
+ it 'configures an identity provider' do
60
61
  expect(subject.dig('data', 'configureIdentityProvider', 'identityProvider', 'domain')).
61
62
  to eq(domain)
62
63
  end
63
64
  end
65
+
66
+ describe 'for the wrong email scoped user' do
67
+ let(:domain) { Faker::Internet.domain_name }
68
+ let(:current_scope) { domain }
69
+
70
+ it 'does not configure an identity provider' do
71
+ expect(subject.dig('errors')).to_not be_empty
72
+ end
73
+ end
64
74
  end
65
75
  end