osso 0.0.5.pre.theta → 0.0.7
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.
- checksums.yaml +4 -4
- data/.buildkite/pipeline.yml +6 -4
- data/.github/dependabot.yml +8 -0
- data/.github/workflows/automerge.yml +19 -0
- data/.rubocop.yml +4 -1
- data/Gemfile +2 -2
- data/Gemfile.lock +58 -37
- data/LICENSE +21 -23
- data/Rakefile +2 -0
- data/bin/annotate +3 -1
- data/db/schema.rb +41 -3
- data/lib/osso.rb +0 -1
- data/lib/osso/db/migrate/20200929154117_add_users_count_to_identity_providers_and_enterprise_accounts.rb +6 -0
- data/lib/osso/db/migrate/20201023142158_add_rodauth_tables.rb +47 -0
- data/lib/osso/db/migrate/20201105122026_add_token_index_to_access_tokens.rb +5 -0
- data/lib/osso/db/migrate/20201106154936_add_requested_to_authorization_codes_and_access_tokens.rb +6 -0
- data/lib/osso/db/migrate/20201109160851_add_sso_issuer_to_identity_providers.rb +12 -0
- data/lib/osso/db/migrate/20201110190754_remove_oauth_client_id_from_enterprise_accounts.rb +9 -0
- data/lib/osso/db/migrate/20201112160120_add_ping_to_identity_provider_service_enum.rb +28 -0
- data/lib/osso/db/migrate/20201125143501_add_salesforce_to_provider_service_enum.rb +28 -0
- data/lib/osso/error/account_configuration_error.rb +1 -0
- data/lib/osso/error/oauth_error.rb +6 -3
- data/lib/osso/graphql/mutation.rb +2 -0
- data/lib/osso/graphql/mutations.rb +2 -0
- data/lib/osso/graphql/mutations/create_enterprise_account.rb +0 -7
- data/lib/osso/graphql/mutations/create_identity_provider.rb +7 -6
- data/lib/osso/graphql/mutations/delete_identity_provider.rb +24 -0
- data/lib/osso/graphql/mutations/invite_admin_user.rb +43 -0
- data/lib/osso/graphql/query.rb +8 -0
- data/lib/osso/graphql/resolvers/enterprise_accounts.rb +3 -3
- data/lib/osso/graphql/types.rb +2 -2
- data/lib/osso/graphql/types/admin_user.rb +9 -0
- data/lib/osso/graphql/types/base_object.rb +1 -1
- data/lib/osso/graphql/types/enterprise_account.rb +1 -0
- data/lib/osso/graphql/types/identity_provider.rb +2 -0
- data/lib/osso/graphql/types/identity_provider_service.rb +3 -1
- data/lib/osso/lib/app_config.rb +1 -1
- data/lib/osso/lib/route_map.rb +0 -16
- data/lib/osso/lib/saml_handler.rb +5 -0
- data/lib/osso/models/access_token.rb +4 -2
- data/lib/osso/models/account.rb +34 -0
- data/lib/osso/models/authorization_code.rb +2 -1
- data/lib/osso/models/enterprise_account.rb +3 -1
- data/lib/osso/models/identity_provider.rb +19 -5
- data/lib/osso/models/models.rb +1 -0
- data/lib/osso/models/oauth_client.rb +0 -1
- data/lib/osso/models/user.rb +2 -2
- data/lib/osso/routes/admin.rb +39 -33
- data/lib/osso/routes/auth.rb +9 -9
- data/lib/osso/routes/oauth.rb +35 -17
- data/lib/osso/version.rb +1 -1
- data/lib/osso/views/admin.erb +5 -0
- data/lib/osso/views/error.erb +1 -0
- data/lib/osso/views/layout.erb +0 -0
- data/lib/osso/views/multiple_providers.erb +1 -0
- data/lib/osso/views/welcome.erb +0 -0
- data/lib/tasks/bootstrap.rake +25 -4
- data/osso-rb.gemspec +5 -0
- data/spec/factories/account.rb +24 -0
- data/spec/factories/enterprise_account.rb +11 -3
- data/spec/factories/identity_providers.rb +10 -2
- data/spec/factories/user.rb +4 -0
- data/spec/graphql/mutations/configure_identity_provider_spec.rb +1 -1
- data/spec/graphql/mutations/create_enterprise_account_spec.rb +0 -14
- data/spec/graphql/mutations/create_identity_provider_spec.rb +59 -8
- data/spec/graphql/query/identity_provider_spec.rb +2 -2
- data/spec/models/enterprise_account_spec.rb +18 -0
- data/spec/models/identity_provider_spec.rb +24 -3
- data/spec/routes/admin_spec.rb +7 -41
- data/spec/routes/auth_spec.rb +17 -18
- data/spec/routes/oauth_spec.rb +88 -5
- data/spec/spec_helper.rb +3 -3
- data/spec/support/views/layout.erb +1 -0
- data/spec/support/views/multiple_providers.erb +1 -0
- metadata +107 -7
- data/lib/osso/helpers/auth.rb +0 -94
- data/lib/osso/helpers/helpers.rb +0 -8
- data/spec/helpers/auth_spec.rb +0 -269
data/lib/osso.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rodauth/migrations'
|
2
|
+
|
3
|
+
class AddRodauthTables < ActiveRecord::Migration[6.0]
|
4
|
+
DB = Sequel.postgres(extensions: :activerecord_connection)
|
5
|
+
|
6
|
+
def change
|
7
|
+
enable_extension "citext"
|
8
|
+
|
9
|
+
create_table :accounts, id: :uuid do |t|
|
10
|
+
t.citext :email, null: false, index: { unique: true, where: "status_id IN (1, 2)" }
|
11
|
+
t.integer :status_id, null: false, default: 1
|
12
|
+
t.string :role, null: false, default: 'admin'
|
13
|
+
t.string :oauth_client_id, null: true, index: true
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :account_password_hashes, id: :uuid do |t|
|
17
|
+
t.foreign_key :accounts, column: :id
|
18
|
+
t.string :password_hash, null: false
|
19
|
+
end
|
20
|
+
|
21
|
+
Rodauth.create_database_authentication_functions(DB, table_name: "account_password_hashes")
|
22
|
+
|
23
|
+
# Used by the password reset feature
|
24
|
+
create_table :account_password_reset_keys, id: :uuid do |t|
|
25
|
+
t.foreign_key :accounts, column: :id
|
26
|
+
t.string :key, null: false
|
27
|
+
t.datetime :deadline, null: false
|
28
|
+
t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Used by the account verification feature
|
32
|
+
create_table :account_verification_keys, id: :uuid do |t|
|
33
|
+
t.string :key, null: false
|
34
|
+
t.datetime :requested_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
35
|
+
t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
36
|
+
end
|
37
|
+
|
38
|
+
add_reference :account_verification_keys, :account, type: :uuid, index: true
|
39
|
+
|
40
|
+
# Used by the remember me feature
|
41
|
+
create_table :account_remember_keys, id: :uuid do |t|
|
42
|
+
t.foreign_key :accounts, column: :id
|
43
|
+
t.string :key, null: false
|
44
|
+
t.datetime :deadline, null: false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddSsoIssuerToIdentityProviders < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
add_column :identity_providers, :sso_issuer, :string
|
4
|
+
|
5
|
+
Osso::Models::IdentityProvider.all.each do |idp|
|
6
|
+
idp.sso_issuer = idp.root_url + "/" + idp.domain
|
7
|
+
idp.save
|
8
|
+
end
|
9
|
+
|
10
|
+
change_column_null :identity_providers, :sso_issuer, false
|
11
|
+
end
|
12
|
+
end
|
@@ -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
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class AddSalesforceToProviderServiceEnum < ActiveRecord::Migration[6.0]
|
2
|
+
disable_ddl_transaction!
|
3
|
+
|
4
|
+
def up
|
5
|
+
execute <<-SQL
|
6
|
+
ALTER TYPE identity_provider_service ADD VALUE 'SALESFORCE';
|
7
|
+
SQL
|
8
|
+
end
|
9
|
+
|
10
|
+
def down
|
11
|
+
execute <<~SQL
|
12
|
+
CREATE TYPE identity_provider_service_new AS ENUM ('AZURE', 'OKTA', 'ONELOGIN', 'GOOGLE', 'PING');
|
13
|
+
|
14
|
+
-- Remove values that won't be compatible with new definition
|
15
|
+
DELETE FROM identity_providers WHERE service = 'SALESFORCE';
|
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
|
@@ -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
|
38
|
-
|
39
|
-
|
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,
|
16
|
-
customer = enterprise_account(
|
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:
|
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(
|
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
|
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
|
data/lib/osso/graphql/query.rb
CHANGED
@@ -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
|
data/lib/osso/graphql/types.rb
CHANGED
@@ -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.
|
31
|
+
return true if object.instance_of?(Hash)
|
32
32
|
|
33
33
|
internal_authorized?(context) || enterprise_authorized?(context, object&.domain)
|
34
34
|
end
|
@@ -13,10 +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
|
19
20
|
field :acs_url_validator, String, null: false
|
21
|
+
field :oauth_client, Types::OauthClient, null: false
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|