osso 0.0.5.pre.zeta → 0.0.5

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +4 -2
  3. data/.rubocop.yml +4 -1
  4. data/Gemfile.lock +48 -32
  5. data/LICENSE +21 -23
  6. data/bin/annotate +3 -1
  7. data/db/schema.rb +41 -3
  8. data/lib/osso/db/migrate/20200929154117_add_users_count_to_identity_providers_and_enterprise_accounts.rb +6 -0
  9. data/lib/osso/db/migrate/20201023142158_add_rodauth_tables.rb +47 -0
  10. data/lib/osso/db/migrate/20201105122026_add_token_index_to_access_tokens.rb +5 -0
  11. data/lib/osso/db/migrate/20201106154936_add_requested_to_authorization_codes_and_access_tokens.rb +6 -0
  12. data/lib/osso/db/migrate/20201109160851_add_sso_issuer_to_identity_providers.rb +12 -0
  13. data/lib/osso/db/migrate/20201110190754_remove_oauth_client_id_from_enterprise_accounts.rb +9 -0
  14. data/lib/osso/db/migrate/20201112160120_add_ping_to_identity_provider_service_enum.rb +28 -0
  15. data/lib/osso/error/account_configuration_error.rb +1 -0
  16. data/lib/osso/error/oauth_error.rb +6 -3
  17. data/lib/osso/graphql/mutation.rb +2 -0
  18. data/lib/osso/graphql/mutations.rb +2 -0
  19. data/lib/osso/graphql/mutations/create_enterprise_account.rb +0 -7
  20. data/lib/osso/graphql/mutations/create_identity_provider.rb +7 -6
  21. data/lib/osso/graphql/mutations/delete_identity_provider.rb +24 -0
  22. data/lib/osso/graphql/mutations/invite_admin_user.rb +43 -0
  23. data/lib/osso/graphql/query.rb +8 -0
  24. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +3 -3
  25. data/lib/osso/graphql/types.rb +2 -2
  26. data/lib/osso/graphql/types/admin_user.rb +9 -0
  27. data/lib/osso/graphql/types/base_object.rb +1 -1
  28. data/lib/osso/graphql/types/enterprise_account.rb +1 -0
  29. data/lib/osso/graphql/types/identity_provider.rb +3 -0
  30. data/lib/osso/graphql/types/identity_provider_service.rb +2 -1
  31. data/lib/osso/helpers/auth.rb +1 -1
  32. data/lib/osso/lib/route_map.rb +0 -15
  33. data/lib/osso/lib/saml_handler.rb +5 -0
  34. data/lib/osso/models/access_token.rb +4 -2
  35. data/lib/osso/models/account.rb +34 -0
  36. data/lib/osso/models/authorization_code.rb +2 -1
  37. data/lib/osso/models/enterprise_account.rb +3 -1
  38. data/lib/osso/models/identity_provider.rb +23 -5
  39. data/lib/osso/models/models.rb +1 -0
  40. data/lib/osso/models/oauth_client.rb +0 -1
  41. data/lib/osso/models/user.rb +2 -2
  42. data/lib/osso/routes/admin.rb +39 -33
  43. data/lib/osso/routes/auth.rb +9 -9
  44. data/lib/osso/routes/oauth.rb +35 -17
  45. data/lib/osso/version.rb +1 -1
  46. data/lib/osso/views/admin.erb +5 -0
  47. data/lib/osso/views/error.erb +1 -0
  48. data/lib/osso/views/layout.erb +0 -0
  49. data/lib/osso/views/multiple_providers.erb +1 -0
  50. data/lib/osso/views/welcome.erb +0 -0
  51. data/lib/tasks/bootstrap.rake +18 -4
  52. data/osso-rb.gemspec +5 -0
  53. data/spec/factories/account.rb +24 -0
  54. data/spec/factories/enterprise_account.rb +11 -3
  55. data/spec/factories/identity_providers.rb +10 -2
  56. data/spec/factories/user.rb +4 -0
  57. data/spec/graphql/mutations/configure_identity_provider_spec.rb +1 -1
  58. data/spec/graphql/mutations/create_enterprise_account_spec.rb +0 -14
  59. data/spec/graphql/mutations/create_identity_provider_spec.rb +59 -8
  60. data/spec/graphql/query/identity_provider_spec.rb +3 -2
  61. data/spec/models/enterprise_account_spec.rb +18 -0
  62. data/spec/models/identity_provider_spec.rb +35 -1
  63. data/spec/routes/admin_spec.rb +7 -41
  64. data/spec/routes/auth_spec.rb +17 -18
  65. data/spec/routes/oauth_spec.rb +88 -5
  66. data/spec/spec_helper.rb +3 -3
  67. data/spec/support/views/layout.erb +1 -0
  68. data/spec/support/views/multiple_providers.erb +1 -0
  69. metadata +92 -5
  70. data/spec/helpers/auth_spec.rb +0 -97
@@ -0,0 +1,9 @@
1
+ class RemoveOauthClientIdFromEnterpriseAccounts < ActiveRecord::Migration[6.0]
2
+ def up
3
+ remove_reference :enterprise_accounts, :oauth_client, index: true
4
+ end
5
+
6
+ def down
7
+ add_reference :enterprise_accounts, :oauth_client, type: :uuid, index: true
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ class AddPingToIdentityProviderServiceEnum < ActiveRecord::Migration[6.0]
2
+ disable_ddl_transaction!
3
+
4
+ def up
5
+ execute <<-SQL
6
+ ALTER TYPE identity_provider_service ADD VALUE 'PING';
7
+ SQL
8
+ end
9
+
10
+ def down
11
+ execute <<~SQL
12
+ CREATE TYPE identity_provider_service_new AS ENUM ('AZURE', 'OKTA', 'ONELOGIN', 'GOOGLE');
13
+
14
+ -- Remove values that won't be compatible with new definition
15
+ DELETE FROM identity_providers WHERE service = 'PING';
16
+
17
+ -- Convert to new type, casting via text representation
18
+ ALTER TABLE identity_providers
19
+ ALTER COLUMN service TYPE identity_provider_service_new
20
+ USING (service::text::identity_provider_service_new);
21
+
22
+ -- and swap the types
23
+ DROP TYPE identity_provider_service;
24
+
25
+ ALTER TYPE identity_provider_service_new RENAME TO identity_provider_service;
26
+ SQL
27
+ end
28
+ end
@@ -6,6 +6,7 @@ module Osso
6
6
 
7
7
  class MissingConfiguredIdentityProvider < AccountConfigurationError
8
8
  def initialize(domain: 'The requested domain')
9
+ super
9
10
  @domain = domain
10
11
  end
11
12
 
@@ -10,6 +10,7 @@ module Osso
10
10
 
11
11
  class NoAccountForOAuthClientError < OAuthError
12
12
  def initialize(domain: 'the requested domain')
13
+ super
13
14
  @domain = domain
14
15
  end
15
16
 
@@ -30,13 +31,15 @@ module Osso
30
31
 
31
32
  class InvalidRedirectUri < OAuthError
32
33
  def initialize(redirect_uri:)
34
+ super
33
35
  @redirect_uri = redirect_uri
34
36
  end
35
37
 
36
38
  def message
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).'
39
+ "The requested redirect URI #{@redirect_uri} is not on the allow-list for the rquested " \
40
+ 'OAuth client identifier. Review our OAuth documentation, check you\'re using the correct ' \
41
+ 'OAuth client identifier, and confirm your Redirect URI allow-list includes the ' \
42
+ 'appropriate URI(s).'
40
43
  end
41
44
  end
42
45
  end
@@ -11,7 +11,9 @@ 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
16
+ field :invite_admin_user, mutation: Mutations::InviteAdminUser
15
17
  field :set_redirect_uris, mutation: Mutations::SetRedirectUris
16
18
  field :regenerate_oauth_credentials, mutation: Mutations::RegenerateOauthCredentials
17
19
  field :update_app_config, mutation: Mutations::UpdateAppConfig
@@ -11,7 +11,9 @@ 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'
16
+ require_relative 'mutations/invite_admin_user'
15
17
  require_relative 'mutations/regenerate_oauth_credentials'
16
18
  require_relative 'mutations/set_redirect_uris'
17
19
  require_relative 'mutations/update_app_config'
@@ -8,24 +8,17 @@ module Osso
8
8
 
9
9
  argument :domain, String, required: true
10
10
  argument :name, String, required: true
11
- argument :oauth_client_id, String, required: false
12
11
 
13
12
  field :enterprise_account, Types::EnterpriseAccount, null: false
14
13
  field :errors, [String], null: false
15
14
 
16
15
  def resolve(**args)
17
16
  enterprise_account = Osso::Models::EnterpriseAccount.new(args)
18
- enterprise_account.oauth_client_id ||= find_client_db_id(context[:oauth_client_id])
19
17
 
20
18
  return response_data(enterprise_account: enterprise_account) if enterprise_account.save
21
19
 
22
20
  response_error(enterprise_account.errors)
23
21
  end
24
-
25
- def find_client_db_id(oauth_client_identifier)
26
- Osso::Models::OauthClient.find_by(identifier: oauth_client_identifier).
27
- id
28
- end
29
22
  end
30
23
  end
31
24
  end
@@ -8,17 +8,18 @@ module Osso
8
8
 
9
9
  argument :enterprise_account_id, ID, required: true
10
10
  argument :service, Types::IdentityProviderService, required: false
11
+ argument :oauth_client_id, String, required: true
11
12
 
12
13
  field :identity_provider, Types::IdentityProvider, null: false
13
14
  field :errors, [String], null: false
14
15
 
15
- def resolve(service: nil, **args)
16
- customer = enterprise_account(**args)
16
+ def resolve(service: nil, enterprise_account_id:, oauth_client_id:)
17
+ customer = enterprise_account(enterprise_account_id: enterprise_account_id)
17
18
 
18
19
  identity_provider = customer.identity_providers.build(
19
20
  service: service,
20
21
  domain: customer.domain,
21
- oauth_client_id: customer.oauth_client_id,
22
+ oauth_client_id: oauth_client_id,
22
23
  )
23
24
 
24
25
  return response_data(identity_provider: identity_provider) if identity_provider.save
@@ -26,11 +27,11 @@ module Osso
26
27
  response_error(identity_provider.errors)
27
28
  end
28
29
 
29
- def domain(**args)
30
- enterprise_account(**args)&.domain
30
+ def domain(enterprise_account_id:, **args)
31
+ enterprise_account(enterprise_account_id: enterprise_account_id)&.domain
31
32
  end
32
33
 
33
- def enterprise_account(enterprise_account_id:, **_args)
34
+ def enterprise_account(enterprise_account_id:)
34
35
  @enterprise_account ||= Osso::Models::EnterpriseAccount.find(enterprise_account_id)
35
36
  end
36
37
  end
@@ -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
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class InviteAdminUser < BaseMutation
7
+ null false
8
+
9
+ argument :email, String, required: true
10
+ argument :oauth_client_id, ID, required: false
11
+ argument :role, String, required: true
12
+
13
+ field :admin_user, Types::AdminUser, null: true
14
+ field :errors, [String], null: false
15
+
16
+ def resolve(email:, role:, oauth_client_id: nil)
17
+ admin_user = Osso::Models::Account.new(
18
+ email: email,
19
+ role: role,
20
+ oauth_client_id: oauth_client_id,
21
+ )
22
+
23
+ if admin_user.save
24
+ verify_user(email)
25
+
26
+ return response_data(admin_user: admin_user)
27
+ end
28
+
29
+ response_error(admin_user.errors)
30
+ end
31
+
32
+ def ready?(*)
33
+ admin_ready?
34
+ end
35
+
36
+ def verify_user(email)
37
+ context[:rodauth].account_from_login(email)
38
+ context[:rodauth].setup_account_verification
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,6 +5,7 @@ module Osso
5
5
  module Types
6
6
  class QueryType < ::GraphQL::Schema::Object
7
7
  field :enterprise_accounts, null: true, resolver: Resolvers::EnterpriseAccounts do
8
+ argument :search, String, required: false
8
9
  argument :sort_column, String, required: false
9
10
  argument :sort_order, String, required: false
10
11
  end
@@ -40,6 +41,13 @@ module Osso
40
41
  argument :id, ID, required: true
41
42
  end
42
43
 
44
+ field(
45
+ :admin_users,
46
+ [Types::AdminUser],
47
+ null: false,
48
+ resolve: ->(_obj, _args, _context) { Osso::Models::Account.all },
49
+ )
50
+
43
51
  field(
44
52
  :current_user,
45
53
  Types::AdminUser,
@@ -6,12 +6,12 @@ module Osso
6
6
  class EnterpriseAccounts < BaseResolver
7
7
  type Types::EnterpriseAccount.connection_type, null: true
8
8
 
9
- def resolve(sort_column: nil, sort_order: nil)
9
+ def resolve(sort_column: nil, sort_order: nil, search: nil)
10
10
  return Array(Osso::Models::EnterpriseAccount.find_by(domain: context_domain)) unless internal_authorized?
11
11
 
12
12
  accounts = Osso::Models::EnterpriseAccount
13
-
14
- accounts = accounts.order(sort_column => sort_order_sym(sort_order)) if sort_column && sort_order
13
+ accounts = accounts.where('domain ilike ? OR name ilike ?', "%#{search}%", "%#{search}%") if search
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,8 +14,8 @@ require_relative 'types/app_config'
14
14
  require_relative 'types/error'
15
15
  require_relative 'types/identity_provider_service'
16
16
  require_relative 'types/identity_provider_status'
17
+ require_relative 'types/redirect_uri'
18
+ require_relative 'types/oauth_client'
17
19
  require_relative 'types/identity_provider'
18
20
  require_relative 'types/enterprise_account'
19
- require_relative 'types/redirect_uri'
20
21
  require_relative 'types/redirect_uri_input'
21
- require_relative 'types/oauth_client'
@@ -11,11 +11,20 @@ module Osso
11
11
  field :id, ID, null: false
12
12
  field :email, String, null: false
13
13
  field :scope, String, null: false
14
+ field :role, String, null: false
14
15
  field :oauth_client_id, ID, null: true
15
16
 
16
17
  def self.authorized?(_object, _context)
17
18
  true
18
19
  end
20
+
21
+ def created_at
22
+ 12.hours.ago
23
+ end
24
+
25
+ def updated_at
26
+ 12.hours.ago
27
+ end
19
28
  end
20
29
  end
21
30
  end
@@ -28,7 +28,7 @@ module Osso
28
28
  def self.authorized?(object, context)
29
29
  # we first receive the payload object as a hash, but can depend on the
30
30
  # return type to hide the actual objects non-admins shouldn't see
31
- return true if object.class == Hash
31
+ return true if object.instance_of?(Hash)
32
32
 
33
33
  internal_authorized?(context) || enterprise_authorized?(context, object&.domain)
34
34
  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'
@@ -13,9 +13,12 @@ module Osso
13
13
  field :service, Types::IdentityProviderService, null: true
14
14
  field :domain, String, null: false
15
15
  field :acs_url, String, null: false
16
+ field :sso_issuer, String, null: false
16
17
  field :sso_url, String, null: true
17
18
  field :sso_cert, String, null: true
18
19
  field :status, Types::IdentityProviderStatus, null: false
20
+ field :acs_url_validator, String, null: false
21
+ field :oauth_client, Types::OauthClient, null: false
19
22
  end
20
23
  end
21
24
  end
@@ -5,9 +5,10 @@ module Osso
5
5
  module Types
6
6
  class IdentityProviderService < BaseEnum
7
7
  value('AZURE', 'Microsoft Azure Identity Provider', value: 'AZURE')
8
+ value('GOOGLE', 'Google SAML Identity Provider', value: 'GOOGLE')
8
9
  value('OKTA', 'Okta Identity Provider', value: 'OKTA')
9
10
  value('ONELOGIN', 'OneLogin Identity Provider', value: 'ONELOGIN')
10
- value('GOOGLE', 'Google SAML Identity Provider', value: 'GOOGLE')
11
+ value('PING', 'PingID Identity Provider', value: 'PING')
11
12
  end
12
13
  end
13
14
  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
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/MethodLength
4
-
5
3
  module Osso
6
4
  module RouteMap
7
5
  def self.included(klass)
@@ -9,20 +7,7 @@ module Osso
9
7
  use Osso::Admin
10
8
  use Osso::Auth
11
9
  use Osso::Oauth
12
-
13
- post '/graphql' do
14
- token_protected!
15
-
16
- result = Osso::GraphQL::Schema.execute(
17
- params[:query],
18
- variables: params[:variables],
19
- context: current_user.symbolize_keys,
20
- )
21
-
22
- json result
23
- end
24
10
  end
25
11
  end
26
12
  end
27
13
  end
28
- # rubocop:enable Metrics/MethodLength
@@ -55,6 +55,7 @@ module Osso
55
55
  @authorization_code ||= user.authorization_codes.create!(
56
56
  oauth_client: provider.oauth_client,
57
57
  redirect_uri: redirect_uri_base,
58
+ requested: requested_param,
58
59
  )
59
60
  end
60
61
 
@@ -81,5 +82,9 @@ module Osso
81
82
  def valid_idp_initiated_flow
82
83
  !session[:osso_oauth_redirect_uri] && !session[:osso_oauth_state]
83
84
  end
85
+
86
+ def requested_param
87
+ @session.delete(:osso_oauth_requested)
88
+ end
84
89
  end
85
90
  end
@@ -39,9 +39,11 @@ end
39
39
  # updated_at :datetime not null
40
40
  # user_id :uuid
41
41
  # oauth_client_id :uuid
42
+ # requested :jsonb
42
43
  #
43
44
  # Indexes
44
45
  #
45
- # index_access_tokens_on_oauth_client_id (oauth_client_id)
46
- # index_access_tokens_on_user_id (user_id)
46
+ # index_access_tokens_on_oauth_client_id (oauth_client_id)
47
+ # index_access_tokens_on_token_and_expires_at (token,expires_at) UNIQUE
48
+ # index_access_tokens_on_user_id (user_id)
47
49
  #
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Models
5
+ class Account < ::ActiveRecord::Base
6
+ enum status_id: { 1 => :Unverified, 2 => :Verified, 3 => :Closed }
7
+
8
+ def context
9
+ {
10
+ email: email,
11
+ id: id,
12
+ scope: role,
13
+ oauth_client_id: oauth_client_id,
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # == Schema Information
21
+ #
22
+ # Table name: accounts
23
+ #
24
+ # id :uuid not null, primary key
25
+ # email :citext not null
26
+ # status_id :integer default(NULL), not null
27
+ # role :string default("admin"), not null
28
+ # oauth_client_id :string
29
+ #
30
+ # Indexes
31
+ #
32
+ # index_accounts_on_email (email) UNIQUE WHERE (status_id = ANY (ARRAY[1, 2]))
33
+ # index_accounts_on_oauth_client_id (oauth_client_id)
34
+ #