osso 0.0.3.14 → 0.0.3.19
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 +17 -1
- data/.rubocop.yml +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +10 -2
- data/README.md +3 -2
- data/bin/publish +18 -0
- data/db/schema.rb +9 -1
- data/lib/osso/db/migrate/20200826201852_create_app_config.rb +11 -0
- data/lib/osso/graphql/mutation.rb +7 -0
- data/lib/osso/graphql/mutations.rb +2 -0
- data/lib/osso/graphql/mutations/base_mutation.rb +18 -5
- data/lib/osso/graphql/mutations/configure_identity_provider.rb +8 -10
- data/lib/osso/graphql/mutations/create_enterprise_account.rb +7 -0
- data/lib/osso/graphql/mutations/create_identity_provider.rb +14 -5
- data/lib/osso/graphql/mutations/create_oauth_client.rb +1 -3
- data/lib/osso/graphql/mutations/delete_enterprise_account.rb +9 -11
- data/lib/osso/graphql/mutations/delete_oauth_client.rb +1 -3
- data/lib/osso/graphql/mutations/regenerate_oauth_credentials.rb +1 -3
- data/lib/osso/graphql/mutations/set_redirect_uris.rb +2 -4
- data/lib/osso/graphql/mutations/update_app_config.rb +30 -0
- data/lib/osso/graphql/query.rb +14 -0
- data/lib/osso/graphql/resolvers.rb +1 -0
- data/lib/osso/graphql/resolvers/base_resolver.rb +21 -0
- data/lib/osso/graphql/resolvers/enterprise_account.rb +1 -11
- data/lib/osso/graphql/resolvers/enterprise_accounts.rb +2 -2
- data/lib/osso/graphql/resolvers/oauth_clients.rb +2 -2
- data/lib/osso/graphql/types.rb +2 -1
- data/lib/osso/graphql/types/admin_user.rb +22 -0
- data/lib/osso/graphql/types/app_config.rb +22 -0
- data/lib/osso/graphql/types/base_object.rb +22 -0
- data/lib/osso/graphql/types/enterprise_account.rb +0 -5
- data/lib/osso/graphql/types/identity_provider.rb +0 -6
- data/lib/osso/graphql/types/oauth_client.rb +2 -4
- data/lib/osso/graphql/types/redirect_uri.rb +2 -4
- data/lib/osso/helpers/auth.rb +40 -18
- data/lib/osso/lib/route_map.rb +2 -2
- data/lib/osso/models/app_config.rb +33 -0
- data/lib/osso/models/identity_provider.rb +6 -12
- data/lib/osso/models/models.rb +1 -0
- data/lib/osso/models/oauth_client.rb +1 -0
- data/lib/osso/models/redirect_uri.rb +0 -11
- data/lib/osso/routes/admin.rb +2 -2
- data/lib/osso/routes/auth.rb +29 -12
- data/lib/osso/routes/oauth.rb +25 -18
- data/lib/osso/version.rb +1 -1
- data/lib/tasks/bootstrap.rake +2 -0
- data/spec/graphql/mutations/configure_identity_provider_spec.rb +17 -4
- data/spec/graphql/mutations/create_enterprise_account_spec.rb +53 -4
- data/spec/graphql/mutations/create_identity_provider_spec.rb +18 -6
- data/spec/graphql/mutations/create_oauth_client_spec.rb +10 -3
- data/spec/graphql/mutations/delete_enterprise_account_spec.rb +18 -4
- data/spec/graphql/mutations/delete_oauth_client_spec.rb +8 -4
- data/spec/graphql/query/enterprise_account_spec.rb +21 -6
- data/spec/graphql/query/enterprise_accounts_spec.rb +4 -2
- data/spec/graphql/query/identity_provider_spec.rb +16 -6
- data/spec/graphql/query/oauth_clients_spec.rb +10 -7
- data/spec/helpers/auth_spec.rb +97 -0
- data/spec/models/identity_provider_spec.rb +12 -0
- data/spec/routes/auth_spec.rb +18 -0
- data/spec/routes/oauth_spec.rb +5 -2
- data/spec/spec_helper.rb +3 -0
- data/spec/support/views/error.erb +0 -0
- metadata +15 -6
- data/lib/osso/graphql/types/user.rb +0 -17
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Osso
|
4
|
+
module GraphQL
|
5
|
+
module Resolvers
|
6
|
+
class BaseResolver < ::GraphQL::Schema::Resolver
|
7
|
+
def admin_authorized?
|
8
|
+
context[:scope] == 'admin'
|
9
|
+
end
|
10
|
+
|
11
|
+
def internal_authorized?
|
12
|
+
%w[admin internal].include?(context[:scope])
|
13
|
+
end
|
14
|
+
|
15
|
+
def enterprise_authorized?(domain)
|
16
|
+
context[:scope] == domain
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,22 +3,12 @@
|
|
3
3
|
module Osso
|
4
4
|
module GraphQL
|
5
5
|
module Resolvers
|
6
|
-
class EnterpriseAccount <
|
6
|
+
class EnterpriseAccount < BaseResolver
|
7
7
|
type Types::EnterpriseAccount, null: false
|
8
8
|
|
9
9
|
def resolve(args)
|
10
|
-
return unless admin? || enterprise_authorized?(args[:domain])
|
11
|
-
|
12
10
|
Osso::Models::EnterpriseAccount.find_by(domain: args[:domain])
|
13
11
|
end
|
14
|
-
|
15
|
-
def admin?
|
16
|
-
context[:scope] == :admin
|
17
|
-
end
|
18
|
-
|
19
|
-
def enterprise_authorized?(domain)
|
20
|
-
context[:scope] == domain
|
21
|
-
end
|
22
12
|
end
|
23
13
|
end
|
24
14
|
end
|
@@ -3,11 +3,11 @@
|
|
3
3
|
module Osso
|
4
4
|
module GraphQL
|
5
5
|
module Resolvers
|
6
|
-
class EnterpriseAccounts <
|
6
|
+
class EnterpriseAccounts < BaseResolver
|
7
7
|
type Types::EnterpriseAccount.connection_type, null: true
|
8
8
|
|
9
9
|
def resolve(sort_column: nil, sort_order: nil)
|
10
|
-
return Array(Osso::Models::EnterpriseAccount.find_by(domain: context[:scope]))
|
10
|
+
return Array(Osso::Models::EnterpriseAccount.find_by(domain: context[:scope])) unless internal_authorized?
|
11
11
|
|
12
12
|
accounts = Osso::Models::EnterpriseAccount
|
13
13
|
|
@@ -3,11 +3,11 @@
|
|
3
3
|
module Osso
|
4
4
|
module GraphQL
|
5
5
|
module Resolvers
|
6
|
-
class OAuthClients <
|
6
|
+
class OAuthClients < BaseResolver
|
7
7
|
type [Types::OauthClient], null: true
|
8
8
|
|
9
9
|
def resolve
|
10
|
-
|
10
|
+
Osso::Models::OauthClient.all
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/osso/graphql/types.rb
CHANGED
@@ -9,6 +9,8 @@ require_relative 'types/base_connection'
|
|
9
9
|
require_relative 'types/base_object'
|
10
10
|
require_relative 'types/base_enum'
|
11
11
|
require_relative 'types/base_input_object'
|
12
|
+
require_relative 'types/admin_user'
|
13
|
+
require_relative 'types/app_config'
|
12
14
|
require_relative 'types/identity_provider_service'
|
13
15
|
require_relative 'types/identity_provider_status'
|
14
16
|
require_relative 'types/identity_provider'
|
@@ -16,4 +18,3 @@ require_relative 'types/enterprise_account'
|
|
16
18
|
require_relative 'types/redirect_uri'
|
17
19
|
require_relative 'types/redirect_uri_input'
|
18
20
|
require_relative 'types/oauth_client'
|
19
|
-
require_relative 'types/user'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql'
|
4
|
+
|
5
|
+
module Osso
|
6
|
+
module GraphQL
|
7
|
+
module Types
|
8
|
+
class AdminUser < Types::BaseObject
|
9
|
+
description 'An Admin User of Osso'
|
10
|
+
|
11
|
+
field :id, ID, null: false
|
12
|
+
field :email, String, null: false
|
13
|
+
field :scope, String, null: false
|
14
|
+
field :oauth_client_id, ID, null: true
|
15
|
+
|
16
|
+
def self.authorized?(_object, _context)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql'
|
4
|
+
|
5
|
+
module Osso
|
6
|
+
module GraphQL
|
7
|
+
module Types
|
8
|
+
class AppConfig < Types::BaseObject
|
9
|
+
description 'Configuration values for your application'
|
10
|
+
|
11
|
+
field :id, ID, null: false
|
12
|
+
field :name, String, null: true
|
13
|
+
field :logo_url, String, null: true
|
14
|
+
field :contact_email, String, null: true
|
15
|
+
|
16
|
+
def self.authorized?(_object, context)
|
17
|
+
admin_authorized?(context)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -10,6 +10,28 @@ module Osso
|
|
10
10
|
|
11
11
|
field :created_at, ::GraphQL::Types::ISO8601DateTime, null: false
|
12
12
|
field :updated_at, ::GraphQL::Types::ISO8601DateTime, null: false
|
13
|
+
|
14
|
+
def self.admin_authorized?(context)
|
15
|
+
context[:scope] == 'admin'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.internal_authorized?(context)
|
19
|
+
%w[admin internal].include?(context[:scope])
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.enterprise_authorized?(context, domain)
|
23
|
+
return false unless domain
|
24
|
+
|
25
|
+
context[:email].split('@')[1] == domain
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.authorized?(object, context)
|
29
|
+
# we first receive the payload object as a hash, but can depend on the
|
30
|
+
# return type to hide the actual objects non-admins shouldn't see
|
31
|
+
return true if object.class == Hash
|
32
|
+
|
33
|
+
internal_authorized?(context) || enterprise_authorized?(context, object&.domain)
|
34
|
+
end
|
13
35
|
end
|
14
36
|
end
|
15
37
|
end
|
@@ -9,7 +9,6 @@ module Osso
|
|
9
9
|
description 'An Account for a company that wishes to use SAML via Osso'
|
10
10
|
implements ::GraphQL::Types::Relay::Node
|
11
11
|
|
12
|
-
global_id_field :gid
|
13
12
|
field :id, ID, null: false
|
14
13
|
field :name, String, null: false
|
15
14
|
field :domain, String, null: false
|
@@ -23,10 +22,6 @@ module Osso
|
|
23
22
|
def identity_providers
|
24
23
|
object.identity_providers
|
25
24
|
end
|
26
|
-
|
27
|
-
def self.authorized?(object, context)
|
28
|
-
super && (context[:scope] == :admin || object.domain == context[:scope])
|
29
|
-
end
|
30
25
|
end
|
31
26
|
end
|
32
27
|
end
|
@@ -7,9 +7,7 @@ module Osso
|
|
7
7
|
module Types
|
8
8
|
class IdentityProvider < Types::BaseObject
|
9
9
|
description 'Represents a SAML based IDP instance for an EnterpriseAccount'
|
10
|
-
implements ::GraphQL::Types::Relay::Node
|
11
10
|
|
12
|
-
global_id_field :gid
|
13
11
|
field :id, ID, null: false
|
14
12
|
field :enterprise_account_id, ID, null: false
|
15
13
|
field :service, Types::IdentityProviderService, null: true
|
@@ -23,10 +21,6 @@ module Osso
|
|
23
21
|
def documentation_pdf_url
|
24
22
|
ENV['BASE_URL'] + '/identity_provider/documentation/' + @object.id
|
25
23
|
end
|
26
|
-
|
27
|
-
def self.authorized?(object, context)
|
28
|
-
super && (context[:scope] == :admin || object.domain == context[:scope])
|
29
|
-
end
|
30
24
|
end
|
31
25
|
end
|
32
26
|
end
|
@@ -7,9 +7,7 @@ module Osso
|
|
7
7
|
module Types
|
8
8
|
class OauthClient < Types::BaseObject
|
9
9
|
description 'An OAuth client used to consume Osso SAML users'
|
10
|
-
implements ::GraphQL::Types::Relay::Node
|
11
10
|
|
12
|
-
global_id_field :gid
|
13
11
|
field :id, ID, null: false
|
14
12
|
field :name, String, null: false
|
15
13
|
field :client_id, String, null: false
|
@@ -24,8 +22,8 @@ module Osso
|
|
24
22
|
object.secret
|
25
23
|
end
|
26
24
|
|
27
|
-
def self.authorized?(
|
28
|
-
|
25
|
+
def self.authorized?(_object, context)
|
26
|
+
admin_authorized?(context)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
@@ -7,15 +7,13 @@ module Osso
|
|
7
7
|
module Types
|
8
8
|
class RedirectUri < Types::BaseObject
|
9
9
|
description 'An allowed redirect URI for an OauthClient'
|
10
|
-
implements ::GraphQL::Types::Relay::Node
|
11
10
|
|
12
|
-
global_id_field :gid
|
13
11
|
field :id, ID, null: false
|
14
12
|
field :uri, String, null: false
|
15
13
|
field :primary, Boolean, null: false
|
16
14
|
|
17
|
-
def self.authorized?(
|
18
|
-
|
15
|
+
def self.authorized?(_object, context)
|
16
|
+
context[:scope] == 'admin'
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
data/lib/osso/helpers/auth.rb
CHANGED
@@ -3,10 +3,21 @@
|
|
3
3
|
module Osso
|
4
4
|
module Helpers
|
5
5
|
module Auth
|
6
|
-
|
6
|
+
END_USER_SCOPE = 'end-user'
|
7
|
+
INTERNAL_SCOPE = 'internal'
|
8
|
+
ADMIN_SCOPE = 'admin'
|
9
|
+
|
10
|
+
attr_accessor :current_user
|
11
|
+
|
12
|
+
def token_protected!
|
13
|
+
decode(token)
|
14
|
+
rescue JWT::DecodeError
|
15
|
+
halt 401
|
16
|
+
end
|
7
17
|
|
8
18
|
def enterprise_protected!(domain = nil)
|
9
19
|
return if admin_authorized?
|
20
|
+
return if internal_authorized?
|
10
21
|
return if enterprise_authorized?(domain)
|
11
22
|
|
12
23
|
halt 401 if request.post?
|
@@ -14,33 +25,42 @@ module Osso
|
|
14
25
|
redirect ENV['JWT_URL']
|
15
26
|
end
|
16
27
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
payload, _args = decode(token)
|
21
|
-
|
22
|
-
@current_scope = payload['scope']
|
28
|
+
def internal_protected!
|
29
|
+
return if admin_authorized?
|
30
|
+
return if internal_authorized?
|
23
31
|
|
24
|
-
|
25
|
-
rescue JWT::DecodeError
|
26
|
-
false
|
32
|
+
redirect ENV['JWT_URL']
|
27
33
|
end
|
28
34
|
|
29
35
|
def admin_protected!
|
30
|
-
return if admin_authorized?
|
36
|
+
return true if admin_authorized?
|
31
37
|
|
32
38
|
redirect ENV['JWT_URL']
|
33
39
|
end
|
34
40
|
|
35
|
-
|
36
|
-
payload, _args = decode(token)
|
41
|
+
private
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
return true
|
41
|
-
end
|
43
|
+
def enterprise_authorized?(domain)
|
44
|
+
decode(token)
|
42
45
|
|
46
|
+
@current_user[:scope] == END_USER_SCOPE &&
|
47
|
+
@current_user[:email].split('@')[1] == domain
|
48
|
+
rescue JWT::DecodeError
|
43
49
|
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def internal_authorized?
|
53
|
+
decode(token)
|
54
|
+
|
55
|
+
@current_user[:scope] == INTERNAL_SCOPE
|
56
|
+
rescue JWT::DecodeError
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def admin_authorized?
|
61
|
+
decode(token)
|
62
|
+
|
63
|
+
@current_user[:scope] == ADMIN_SCOPE
|
44
64
|
rescue JWT::DecodeError
|
45
65
|
false
|
46
66
|
end
|
@@ -60,12 +80,14 @@ module Osso
|
|
60
80
|
end
|
61
81
|
|
62
82
|
def decode(token)
|
63
|
-
JWT.decode(
|
83
|
+
payload, _args = JWT.decode(
|
64
84
|
token,
|
65
85
|
ENV['JWT_HMAC_SECRET'],
|
66
86
|
true,
|
67
87
|
{ algorithm: 'HS256' },
|
68
88
|
)
|
89
|
+
|
90
|
+
@current_user = payload.symbolize_keys
|
69
91
|
end
|
70
92
|
end
|
71
93
|
end
|
data/lib/osso/lib/route_map.rb
CHANGED
@@ -11,12 +11,12 @@ module Osso
|
|
11
11
|
use Osso::Oauth
|
12
12
|
|
13
13
|
post '/graphql' do
|
14
|
-
|
14
|
+
token_protected!
|
15
15
|
|
16
16
|
result = Osso::GraphQL::Schema.execute(
|
17
17
|
params[:query],
|
18
18
|
variables: params[:variables],
|
19
|
-
context:
|
19
|
+
context: current_user.symbolize_keys,
|
20
20
|
)
|
21
21
|
|
22
22
|
json result
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Osso
|
4
|
+
module Models
|
5
|
+
class AppConfig < ::ActiveRecord::Base
|
6
|
+
validate :limit_to_one, on: :create
|
7
|
+
|
8
|
+
def self.find
|
9
|
+
first
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def limit_to_one
|
15
|
+
return if Osso::Models::AppConfig.count.zero?
|
16
|
+
|
17
|
+
errors[:base] << 'AppConfig already exists'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# == Schema Information
|
24
|
+
#
|
25
|
+
# Table name: app_configs
|
26
|
+
#
|
27
|
+
# id :uuid not null, primary key
|
28
|
+
# contact_email :string
|
29
|
+
# logo_url :string
|
30
|
+
# name :string
|
31
|
+
# created_at :datetime not null
|
32
|
+
# updated_at :datetime not null
|
33
|
+
#
|
@@ -19,20 +19,14 @@ module Osso
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def saml_options
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
{
|
23
|
+
domain: domain,
|
24
|
+
idp_sso_target_url: sso_url,
|
25
|
+
idp_cert: sso_cert,
|
26
|
+
issuer: domain,
|
27
|
+
}
|
27
28
|
end
|
28
29
|
|
29
|
-
# def saml_options
|
30
|
-
# raise(
|
31
|
-
# NoMethodError,
|
32
|
-
# '#saml_options must be defined on each provider specific subclass',
|
33
|
-
# )
|
34
|
-
# end
|
35
|
-
|
36
30
|
def assertion_consumer_service_url
|
37
31
|
[
|
38
32
|
ENV.fetch('BASE_URL'),
|
data/lib/osso/models/models.rb
CHANGED