osso 0.0.3.7 → 0.0.3.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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 +7 -1
  11. data/lib/osso/graphql/mutations.rb +7 -1
  12. data/lib/osso/graphql/mutations/add_redirect_uris_to_oauth_client.rb +39 -0
  13. data/lib/osso/graphql/mutations/configure_identity_provider.rb +1 -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/mutations/delete_redirect_uri.rb +38 -0
  18. data/lib/osso/graphql/mutations/mark_redirect_uri_primary.rb +34 -0
  19. data/lib/osso/graphql/mutations/regenerate_oauth_credentials.rb +31 -0
  20. data/lib/osso/graphql/query.rb +15 -2
  21. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +12 -4
  22. data/lib/osso/graphql/resolvers/oauth_clients.rb +1 -1
  23. data/lib/osso/graphql/types.rb +3 -0
  24. data/lib/osso/graphql/types/base_connection.rb +15 -0
  25. data/lib/osso/graphql/types/base_object.rb +4 -0
  26. data/lib/osso/graphql/types/identity_provider.rb +1 -5
  27. data/lib/osso/graphql/types/identity_provider_status.rb +14 -0
  28. data/lib/osso/graphql/types/oauth_client.rb +14 -1
  29. data/lib/osso/graphql/types/redirect_uri.rb +23 -0
  30. data/lib/osso/helpers/auth.rb +13 -12
  31. data/lib/osso/models/access_token.rb +18 -0
  32. data/lib/osso/models/authorization_code.rb +20 -0
  33. data/lib/osso/models/enterprise_account.rb +20 -0
  34. data/lib/osso/models/identity_provider.rb +29 -0
  35. data/lib/osso/models/models.rb +2 -0
  36. data/lib/osso/models/oauth_client.rb +20 -10
  37. data/lib/osso/models/redirect_uri.rb +17 -0
  38. data/lib/osso/models/user.rb +22 -0
  39. data/lib/osso/routes/admin.rb +6 -0
  40. data/lib/osso/routes/auth.rb +1 -1
  41. data/lib/osso/version.rb +1 -1
  42. data/osso-rb.gemspec +1 -0
  43. data/spec/factories/identity_providers.rb +22 -0
  44. data/spec/graphql/mutations/configure_identity_provider_spec.rb +3 -3
  45. data/spec/graphql/mutations/create_oauth_client_spec.rb +55 -0
  46. data/spec/graphql/mutations/delete_enterprise_account_spec.rb +63 -0
  47. data/spec/graphql/mutations/delete_oauth_client_spec.rb +51 -0
  48. data/spec/graphql/query/enterprise_account_spec.rb +1 -1
  49. data/spec/graphql/query/enterprise_accounts_spec.rb +32 -18
  50. data/spec/graphql/query/identity_provider_spec.rb +2 -2
  51. data/spec/graphql/query/{oauth_clients_account_spec.rb → oauth_clients_spec.rb} +2 -0
  52. data/spec/routes/auth_spec.rb +25 -0
  53. metadata +35 -8
  54. data/lib/osso/db/migrate/20200328143303_create_oauth_tables.rb +0 -57
  55. data/lib/osso/graphql/mutations/set_identity_provider.rb +0 -27
  56. data/lib/osso/models/saml_provider.rb +0 -49
  57. data/lib/osso/models/saml_providers/azure_saml_provider.rb +0 -22
  58. data/lib/osso/models/saml_providers/okta_saml_provider.rb +0 -23
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class MarkRedirectUriPrimary < BaseMutation
7
+ null false
8
+
9
+ argument :id, ID, required: true
10
+
11
+ field :oauth_client, Types::OauthClient, null: true
12
+ field :errors, [String], null: false
13
+
14
+ def resolve(id:)
15
+ redirect_uri = Osso::Models::RedirectUri.find(id)
16
+ oauth_client = redirect_uri.oauth_client
17
+
18
+ oauth_client.redirect_uris.update(primary: false)
19
+ redirect_uri.update(primary: true)
20
+
21
+ response_data(oauth_client: oauth_client.reload)
22
+ rescue StandardError => e
23
+ response_error(errors: e.message)
24
+ end
25
+
26
+ def ready?(*)
27
+ return true if context[:scope] == :admin
28
+
29
+ raise ::GraphQL::ExecutionError, 'Only admin users may mutate OauthClients'
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class RegenerateOauthCredentials < BaseMutation
7
+ null false
8
+
9
+ argument :id, ID, required: true
10
+
11
+ field :oauth_client, Types::OauthClient, null: false
12
+ field :errors, [String], null: false
13
+
14
+ def resolve(id:)
15
+ oauth_client = Osso::Models::OauthClient.find(id)
16
+ oauth_client.generate_secrets
17
+
18
+ return response_data(oauth_client: oauth_client) if oauth_client.save
19
+
20
+ response_error(errors: oauth_client.errors.full_messages)
21
+ end
22
+
23
+ def ready?(*)
24
+ return true if context[:scope] == :admin
25
+
26
+ raise ::GraphQL::ExecutionError, 'Only admin users may mutate OauthClients'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -4,13 +4,17 @@ module Osso
4
4
  module GraphQL
5
5
  module Types
6
6
  class QueryType < ::GraphQL::Schema::Object
7
- field :enterprise_accounts, null: true, resolver: Resolvers::EnterpriseAccounts
8
- field :oauth_clients, null: true, resolver: Resolvers::OAuthClients
7
+ field :enterprise_accounts, null: true, resolver: Resolvers::EnterpriseAccounts do
8
+ argument :sort_column, String, required: false
9
+ argument :sort_order, String, required: false
10
+ end
9
11
 
10
12
  field :enterprise_account, null: true, resolver: Resolvers::EnterpriseAccount do
11
13
  argument :domain, String, required: true
12
14
  end
13
15
 
16
+ field :oauth_clients, null: true, resolver: Resolvers::OAuthClients
17
+
14
18
  field(
15
19
  :identity_provider,
16
20
  Types::IdentityProvider,
@@ -19,6 +23,15 @@ module Osso
19
23
  ) do
20
24
  argument :id, ID, required: true
21
25
  end
26
+
27
+ field(
28
+ :oauth_client,
29
+ Types::OauthClient,
30
+ null: true,
31
+ resolve: ->(_obj, args, _context) { Osso::Models::OauthClient.find(args[:id]) },
32
+ ) do
33
+ argument :id, ID, required: true
34
+ end
22
35
  end
23
36
  end
24
37
  end
@@ -4,12 +4,20 @@ module Osso
4
4
  module GraphQL
5
5
  module Resolvers
6
6
  class EnterpriseAccounts < ::GraphQL::Schema::Resolver
7
- type [Types::EnterpriseAccount], null: true
7
+ type Types::EnterpriseAccount.connection_type, null: true
8
8
 
9
- def resolve
10
- return Osso::Models::EnterpriseAccount.all if context[:scope] == :admin
9
+ def resolve(sort_column: nil, sort_order: nil)
10
+ return Array(Osso::Models::EnterpriseAccount.find_by(domain: context[:scope])) if context[:scope] != :admin
11
11
 
12
- Array(Osso::Models::EnterpriseAccount.find_by(domain: context[:scope]))
12
+ accounts = Osso::Models::EnterpriseAccount
13
+
14
+ accounts = accounts.order(sort_column => sort_order_sym(sort_order)) if sort_column && sort_order
15
+
16
+ accounts.all
17
+ end
18
+
19
+ def sort_order_sym(order_string)
20
+ order_string == 'ascend' ? :asc : :desc
13
21
  end
14
22
  end
15
23
  end
@@ -4,7 +4,7 @@ module Osso
4
4
  module GraphQL
5
5
  module Resolvers
6
6
  class OAuthClients < ::GraphQL::Schema::Resolver
7
- type [Types::OAuthClient], null: true
7
+ type [Types::OauthClient], null: true
8
8
 
9
9
  def resolve
10
10
  return Osso::Models::OauthClient.all if context[:scope] == :admin
@@ -5,11 +5,14 @@ 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'
16
+ require_relative 'types/redirect_uri'
14
17
  require_relative 'types/oauth_client'
15
18
  require_relative 'types/user'
@@ -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
@@ -17,13 +17,9 @@ 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
21
  field :documentation_pdf_url, String, null: true
22
22
 
23
- def configured
24
- !!(@object.sso_url && @object.sso_cert)
25
- end
26
-
27
23
  def documentation_pdf_url
28
24
  ENV['BASE_URL'] + '/identity_provider/documentation/' + @object.id
29
25
  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,19 @@ 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
+ field :redirect_uris, [Types::RedirectUri], null: true
18
+
19
+ def client_id
20
+ object.identifier
21
+ end
22
+
23
+ def client_secret
24
+ object.secret
25
+ end
26
+
27
+ def self.authorized?(object, context)
28
+ super && context[:scope] == :admin
29
+ end
17
30
  end
18
31
  end
19
32
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class RedirectUri < Types::BaseObject
9
+ description 'An allowed redirect URI for an OauthClient'
10
+ implements ::GraphQL::Types::Relay::Node
11
+
12
+ global_id_field :gid
13
+ field :id, ID, null: false
14
+ field :uri, String, null: false
15
+ field :primary, Boolean, null: false
16
+
17
+ def self.authorized?(object, context)
18
+ super && context[:scope] == :admin
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -14,13 +14,10 @@ module Osso
14
14
  redirect ENV['JWT_URL']
15
15
  end
16
16
 
17
+ # use client id in payload to restrict customer
18
+ # users from accessing dev?
17
19
  def enterprise_authorized?(_domain)
18
- payload, _args = JWT.decode(
19
- token,
20
- ENV['JWT_HMAC_SECRET'],
21
- true,
22
- { algorithm: 'HS256' },
23
- )
20
+ payload, _args = decode(token)
24
21
 
25
22
  @current_scope = payload['scope']
26
23
 
@@ -36,12 +33,7 @@ module Osso
36
33
  end
37
34
 
38
35
  def admin_authorized?
39
- payload, _args = JWT.decode(
40
- token,
41
- ENV['JWT_HMAC_SECRET'],
42
- true,
43
- { algorithm: 'HS256' },
44
- )
36
+ payload, _args = decode(token)
45
37
 
46
38
  if payload['scope'] == 'admin'
47
39
  @current_scope = :admin
@@ -66,6 +58,15 @@ module Osso
66
58
 
67
59
  redirect request.path
68
60
  end
61
+
62
+ def decode(token)
63
+ JWT.decode(
64
+ token,
65
+ ENV['JWT_HMAC_SECRET'],
66
+ true,
67
+ { algorithm: 'HS256' },
68
+ )
69
+ end
69
70
  end
70
71
  end
71
72
  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
@@ -9,24 +9,34 @@ module Osso
9
9
  has_many :identity_providers
10
10
  has_many :redirect_uris
11
11
 
12
- before_validation :setup, on: :create
12
+ before_validation :generate_secrets, on: :create
13
13
  validates :name, :secret, presence: true
14
14
  validates :identifier, presence: true, uniqueness: true
15
15
 
16
- def default_redirect_uri
16
+ def primary_redirect_uri
17
17
  redirect_uris.find(&:primary)
18
18
  end
19
19
 
20
- def redirect_uri_values
21
- redirect_uris.map(&:uri)
22
- end
23
-
24
- private
25
-
26
- def setup
20
+ def generate_secrets
27
21
  self.identifier = SecureRandom.hex(16)
28
- self.secret = SecureRandom.hex(64)
22
+ self.secret = SecureRandom.hex(32)
29
23
  end
30
24
  end
31
25
  end
32
26
  end
27
+
28
+ # == Schema Information
29
+ #
30
+ # Table name: oauth_clients
31
+ #
32
+ # id :uuid not null, primary key
33
+ # name :string not null
34
+ # secret :string not null
35
+ # identifier :string not null
36
+ # created_at :datetime not null
37
+ # updated_at :datetime not null
38
+ #
39
+ # Indexes
40
+ #
41
+ # index_oauth_clients_on_identifier (identifier) UNIQUE
42
+ #