osso 0.0.3.2 → 0.0.3.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 +4 -1
- data/.rubocop.yml +1 -2
- data/Gemfile.lock +3 -1
- data/bin/console +4 -3
- data/config/database.yml +2 -2
- data/db/schema.rb +133 -1
- data/lib/osso.rb +2 -0
- data/lib/osso/db/migrate/20200328143305_create_identity_providers.rb +12 -0
- data/lib/osso/db/migrate/20200411184535_add_provider_id_to_users.rb +2 -2
- data/lib/osso/db/migrate/20200411192645_create_enterprise_accounts.rb +1 -1
- data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_accounts_and_identity_providers.rb +6 -0
- data/lib/osso/db/migrate/20200714223226_add_identity_provider_service_enum.rb +17 -0
- data/lib/osso/db/migrate/20200715154211_rename_idp_fields_on_identity_provider_to_sso.rb +6 -0
- data/lib/osso/db/migrate/20200715205801_add_name_to_enterprise_account.rb +5 -0
- data/lib/osso/graphql/mutation.rb +10 -5
- data/lib/osso/graphql/mutations.rb +5 -2
- data/lib/osso/graphql/mutations/base_mutation.rb +35 -14
- data/lib/osso/graphql/mutations/configure_identity_provider.rb +31 -21
- data/lib/osso/graphql/mutations/create_enterprise_account.rb +25 -0
- data/lib/osso/graphql/mutations/create_identity_provider.rb +22 -16
- data/lib/osso/graphql/mutations/set_identity_provider.rb +27 -0
- data/lib/osso/graphql/query.rb +19 -22
- data/lib/osso/graphql/resolvers.rb +5 -1
- data/lib/osso/graphql/resolvers/enterprise_account.rb +16 -12
- data/lib/osso/graphql/resolvers/enterprise_accounts.rb +10 -6
- data/lib/osso/graphql/resolvers/oauth_clients.rb +9 -5
- data/lib/osso/graphql/schema.rb +27 -19
- data/lib/osso/graphql/types.rb +4 -1
- data/lib/osso/graphql/types/base_enum.rb +6 -2
- data/lib/osso/graphql/types/base_input_object.rb +10 -0
- data/lib/osso/graphql/types/base_object.rb +6 -2
- data/lib/osso/graphql/types/enterprise_account.rb +22 -18
- data/lib/osso/graphql/types/identity_provider.rb +26 -25
- data/lib/osso/graphql/types/identity_provider_service.rb +8 -4
- data/lib/osso/graphql/types/oauth_client.rb +13 -9
- data/lib/osso/graphql/types/user.rb +10 -5
- data/lib/osso/helpers/auth.rb +5 -3
- data/lib/osso/lib/app_config.rb +1 -1
- data/lib/osso/lib/route_map.rb +28 -0
- data/lib/osso/models/enterprise_account.rb +4 -4
- data/lib/osso/models/identity_provider.rb +48 -0
- data/lib/osso/models/models.rb +1 -1
- data/lib/osso/models/oauth_client.rb +2 -2
- data/lib/osso/models/saml_provider.rb +13 -16
- data/lib/osso/models/saml_providers/azure_saml_provider.rb +2 -2
- data/lib/osso/models/saml_providers/okta_saml_provider.rb +1 -1
- data/lib/osso/models/user.rb +3 -3
- data/lib/osso/routes/admin.rb +18 -15
- data/lib/osso/routes/auth.rb +30 -27
- data/lib/osso/routes/oauth.rb +50 -45
- data/lib/osso/version.rb +1 -1
- data/osso-rb.gemspec +3 -3
- data/spec/factories/enterprise_account.rb +5 -4
- data/spec/factories/identity_providers.rb +49 -0
- data/spec/factories/user.rb +1 -1
- data/spec/graphql/mutations/configure_identity_provider_spec.rb +75 -0
- data/spec/graphql/mutations/create_enterprise_account_spec.rb +68 -0
- data/spec/graphql/mutations/create_identity_provider_spec.rb +104 -0
- data/spec/graphql/query/enterprise_account_spec.rb +68 -0
- data/spec/graphql/query/enterprise_accounts_spec.rb +44 -0
- data/spec/graphql/query/identity_provider_spec.rb +65 -0
- data/spec/graphql/query/oauth_clients_account_spec.rb +48 -0
- data/spec/models/azure_saml_provider_spec.rb +14 -14
- data/spec/models/identity_provider_spec.rb +17 -0
- data/spec/models/okta_saml_provider_spec.rb +15 -15
- data/spec/routes/admin_spec.rb +2 -0
- data/spec/routes/auth_spec.rb +9 -9
- data/spec/routes/oauth_spec.rb +1 -1
- data/spec/spec_helper.rb +4 -5
- data/spec/support/spec_app.rb +9 -0
- data/spec/support/views/admin.erb +5 -0
- metadata +37 -12
- data/lib/osso/db/migrate/20200411144528_create_saml_providers.rb +0 -13
- data/lib/osso/db/migrate/20200413153029_add_oauth_client_reference_to_saml_providers.rb +0 -5
- data/lib/osso/db/migrate/20200501203026_drop_null_constraints_from_saml_provider.rb +0 -7
- data/lib/osso/db/migrate/20200501204047_drop_acs_url.rb +0 -5
- data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_account.rb +0 -5
- data/lib/osso/db/migrate/20200601131227_drop_null_constraint_from_saml_providers_provider.rb +0 -7
- data/lib/osso/db/schema.rb +0 -132
- data/lib/osso/graphql/mutations/set_saml_provider.rb +0 -23
- data/spec/factories/saml_providers.rb +0 -46
- data/spec/models/saml_provider_spec.rb +0 -31
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Osso
|
4
|
+
module GraphQL
|
5
|
+
module Types
|
6
|
+
class IdentityProviderService < BaseEnum
|
7
|
+
value('AZURE', 'Microsoft Azure Identity Provider', value: 'Osso::Models::AzureSamlProvider')
|
8
|
+
value('OKTA', 'Okta Identity Provider', value: 'Osso::Models::OktaSamlProvider')
|
9
|
+
end
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
@@ -2,15 +2,19 @@
|
|
2
2
|
|
3
3
|
require 'graphql'
|
4
4
|
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
module Osso
|
6
|
+
module GraphQL
|
7
|
+
module Types
|
8
|
+
class OAuthClient < Types::BaseObject
|
9
|
+
description 'An OAuth client used to consume Osso SAML users'
|
10
|
+
implements ::GraphQL::Types::Relay::Node
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
global_id_field :gid
|
13
|
+
field :id, ID, null: false
|
14
|
+
field :name, String, null: false
|
15
|
+
field :client_id, String, null: false
|
16
|
+
field :client_secret, String, null: false
|
17
|
+
end
|
18
|
+
end
|
15
19
|
end
|
16
20
|
end
|
@@ -2,11 +2,16 @@
|
|
2
2
|
|
3
3
|
require 'graphql'
|
4
4
|
require_relative 'base_object'
|
5
|
-
module Types
|
6
|
-
class User < Types::BaseObject
|
7
|
-
description 'A User of the application'
|
8
5
|
|
9
|
-
|
10
|
-
|
6
|
+
module Osso
|
7
|
+
module GraphQL
|
8
|
+
module Types
|
9
|
+
class User < Types::BaseObject
|
10
|
+
description 'A User of the application'
|
11
|
+
|
12
|
+
field :id, ID, null: false
|
13
|
+
field :name, String, null: true
|
14
|
+
end
|
15
|
+
end
|
11
16
|
end
|
12
17
|
end
|
data/lib/osso/helpers/auth.rb
CHANGED
@@ -4,15 +4,17 @@ module Osso
|
|
4
4
|
module Helpers
|
5
5
|
module Auth
|
6
6
|
attr_accessor :current_scope
|
7
|
-
|
7
|
+
|
8
8
|
def enterprise_protected!(domain = nil)
|
9
9
|
return if admin_authorized?
|
10
10
|
return if enterprise_authorized?(domain)
|
11
11
|
|
12
|
+
halt 401 if request.post?
|
13
|
+
|
12
14
|
redirect ENV['JWT_URL']
|
13
15
|
end
|
14
16
|
|
15
|
-
def enterprise_authorized?(
|
17
|
+
def enterprise_authorized?(_domain)
|
16
18
|
payload, _args = JWT.decode(
|
17
19
|
token,
|
18
20
|
ENV['JWT_HMAC_SECRET'],
|
@@ -66,4 +68,4 @@ module Osso
|
|
66
68
|
end
|
67
69
|
end
|
68
70
|
end
|
69
|
-
end
|
71
|
+
end
|
data/lib/osso/lib/app_config.rb
CHANGED
@@ -7,7 +7,7 @@ module Osso
|
|
7
7
|
def self.included(klass)
|
8
8
|
klass.class_eval do
|
9
9
|
use Rack::JSONBodyParser
|
10
|
-
use Rack::Session::Cookie, secret: ENV
|
10
|
+
use Rack::Session::Cookie, secret: ENV['SESSION_SECRET']
|
11
11
|
|
12
12
|
error ActiveRecord::RecordNotFound do
|
13
13
|
status 404
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/MethodLength
|
4
|
+
|
5
|
+
module Osso
|
6
|
+
module RouteMap
|
7
|
+
def self.included(klass)
|
8
|
+
klass.class_eval do
|
9
|
+
use Osso::Admin
|
10
|
+
use Osso::Auth
|
11
|
+
use Osso::Oauth
|
12
|
+
|
13
|
+
post '/graphql' do
|
14
|
+
enterprise_protected!
|
15
|
+
|
16
|
+
result = Osso::GraphQL::Schema.execute(
|
17
|
+
params[:query],
|
18
|
+
variables: params[:variables],
|
19
|
+
context: { scope: current_scope },
|
20
|
+
)
|
21
|
+
|
22
|
+
json result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# rubocop:enable Metrics/MethodLength
|
@@ -10,19 +10,19 @@ module Osso
|
|
10
10
|
class EnterpriseAccount < ActiveRecord::Base
|
11
11
|
belongs_to :oauth_client
|
12
12
|
has_many :users
|
13
|
-
has_many :
|
13
|
+
has_many :identity_providers
|
14
14
|
|
15
15
|
def single_provider?
|
16
|
-
|
16
|
+
identity_providers.one?
|
17
17
|
end
|
18
18
|
|
19
19
|
def provider
|
20
20
|
return nil unless single_provider?
|
21
21
|
|
22
|
-
|
22
|
+
identity_providers.first
|
23
23
|
end
|
24
24
|
|
25
|
-
alias
|
25
|
+
alias identity_provider provider
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Osso
|
4
|
+
module Models
|
5
|
+
# Base class for SAML Providers
|
6
|
+
class IdentityProvider < ActiveRecord::Base
|
7
|
+
NAME_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
|
8
|
+
belongs_to :enterprise_account
|
9
|
+
belongs_to :oauth_client
|
10
|
+
has_many :users
|
11
|
+
|
12
|
+
def name
|
13
|
+
service.titlecase
|
14
|
+
# raise(
|
15
|
+
# NoMethodError,
|
16
|
+
# '#name must be defined on each provider specific subclass',
|
17
|
+
# )
|
18
|
+
end
|
19
|
+
|
20
|
+
def saml_options
|
21
|
+
attributes.slice(
|
22
|
+
'domain',
|
23
|
+
'idp_cert',
|
24
|
+
'idp_sso_target_url',
|
25
|
+
).symbolize_keys
|
26
|
+
end
|
27
|
+
|
28
|
+
# def saml_options
|
29
|
+
# raise(
|
30
|
+
# NoMethodError,
|
31
|
+
# '#saml_options must be defined on each provider specific subclass',
|
32
|
+
# )
|
33
|
+
# end
|
34
|
+
|
35
|
+
def assertion_consumer_service_url
|
36
|
+
[
|
37
|
+
ENV.fetch('BASE_URL'),
|
38
|
+
'auth',
|
39
|
+
'saml',
|
40
|
+
id,
|
41
|
+
'callback',
|
42
|
+
].join('/')
|
43
|
+
end
|
44
|
+
|
45
|
+
alias acs_url assertion_consumer_service_url
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/osso/models/models.rb
CHANGED
@@ -6,7 +6,7 @@ module Osso
|
|
6
6
|
class OauthClient < ActiveRecord::Base
|
7
7
|
has_many :access_tokens
|
8
8
|
has_many :refresh_tokens
|
9
|
-
has_many :
|
9
|
+
has_many :identity_providers
|
10
10
|
has_many :redirect_uris
|
11
11
|
|
12
12
|
before_validation :setup, on: :create
|
@@ -29,4 +29,4 @@ module Osso
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
-
end
|
32
|
+
end
|
@@ -3,28 +3,27 @@
|
|
3
3
|
module Osso
|
4
4
|
module Models
|
5
5
|
# Base class for SAML Providers
|
6
|
-
class
|
6
|
+
class IdentityProvider < ActiveRecord::Base
|
7
7
|
NAME_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
|
8
|
-
self.inheritance_column = :provider
|
9
8
|
belongs_to :enterprise_account
|
10
9
|
belongs_to :oauth_client
|
11
10
|
has_many :users
|
12
11
|
|
13
12
|
before_create :create_enterprise_account
|
14
13
|
|
15
|
-
def name
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
14
|
+
# def name
|
15
|
+
# raise(
|
16
|
+
# NoMethodError,
|
17
|
+
# '#name must be defined on each provider specific subclass',
|
18
|
+
# )
|
19
|
+
# end
|
21
20
|
|
22
|
-
def saml_options
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
21
|
+
# def saml_options
|
22
|
+
# raise(
|
23
|
+
# NoMethodError,
|
24
|
+
# '#saml_options must be defined on each provider specific subclass',
|
25
|
+
# )
|
26
|
+
# end
|
28
27
|
|
29
28
|
def assertion_consumer_service_url
|
30
29
|
[
|
@@ -48,5 +47,3 @@ module Osso
|
|
48
47
|
end
|
49
48
|
end
|
50
49
|
end
|
51
|
-
require_relative 'saml_providers/azure_saml_provider'
|
52
|
-
require_relative 'saml_providers/okta_saml_provider'
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Osso
|
4
4
|
module Models
|
5
5
|
# Subclass for Azure / ADFS IDP instances
|
6
|
-
class AzureSamlProvider < Models::
|
6
|
+
class AzureSamlProvider < Models::IdentityProvider
|
7
7
|
def name
|
8
8
|
'Azure'
|
9
9
|
end
|
@@ -19,4 +19,4 @@ module Osso
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
data/lib/osso/models/user.rb
CHANGED
@@ -4,19 +4,19 @@ module Osso
|
|
4
4
|
module Models
|
5
5
|
class User < ActiveRecord::Base
|
6
6
|
belongs_to :enterprise_account
|
7
|
-
belongs_to :
|
7
|
+
belongs_to :identity_provider
|
8
8
|
has_many :authorization_codes, dependent: :delete_all
|
9
9
|
has_many :access_tokens, dependent: :delete_all
|
10
10
|
|
11
11
|
def oauth_client
|
12
|
-
|
12
|
+
identity_provider.oauth_client
|
13
13
|
end
|
14
14
|
|
15
15
|
def as_json(*)
|
16
16
|
{
|
17
17
|
email: email,
|
18
18
|
id: id,
|
19
|
-
idp:
|
19
|
+
idp: identity_provider.name,
|
20
20
|
}
|
21
21
|
end
|
22
22
|
end
|
data/lib/osso/routes/admin.rb
CHANGED
@@ -6,33 +6,36 @@ module Osso
|
|
6
6
|
class Admin < Sinatra::Base
|
7
7
|
include AppConfig
|
8
8
|
helpers Helpers::Auth
|
9
|
+
register Sinatra::Namespace
|
9
10
|
|
10
11
|
before do
|
11
12
|
chomp_token
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
namespace '/admin' do
|
16
|
+
get '' do
|
17
|
+
admin_protected!
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
+
erb :admin
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
get '/enterprise' do
|
23
|
+
admin_protected!
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
erb :admin
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
get '/enterprise/:domain' do
|
29
|
+
enterprise_protected!(params[:domain])
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
+
erb :admin
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
+
get '/config' do
|
35
|
+
admin_protected!
|
34
36
|
|
35
|
-
|
37
|
+
erb :admin
|
38
|
+
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
data/lib/osso/routes/auth.rb
CHANGED
@@ -8,6 +8,7 @@ require 'omniauth-saml'
|
|
8
8
|
module Osso
|
9
9
|
class Auth < Sinatra::Base
|
10
10
|
include AppConfig
|
11
|
+
register Sinatra::Namespace
|
11
12
|
|
12
13
|
UUID_REGEXP =
|
13
14
|
/[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/.
|
@@ -24,41 +25,43 @@ module Osso
|
|
24
25
|
identity_provider_id_regex: UUID_REGEXP,
|
25
26
|
path_prefix: '/saml',
|
26
27
|
callback_suffix: 'callback',
|
27
|
-
) do |
|
28
|
-
provider = Models::
|
28
|
+
) do |identity_provider_id, _env|
|
29
|
+
provider = Models::IdentityProvider.find(identity_provider_id)
|
29
30
|
provider.saml_options
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
namespace '/auth' do
|
35
|
+
# Enterprise users are sent here after authenticating against
|
36
|
+
# their Identity Provider. We find or create a user record,
|
37
|
+
# and then create an authorization code for that user. The user
|
38
|
+
# is redirected back to your application with this code
|
39
|
+
# as a URL query param, which you then exhange for an access token
|
40
|
+
post '/saml/:id/callback' do
|
41
|
+
provider = Models::IdentityProvider.find(params[:id])
|
42
|
+
oauth_client = provider.oauth_client
|
43
|
+
redirect_uri = env['redirect_uri'] || oauth_client.default_redirect_uri.uri
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
attributes = env['omniauth.auth']&.
|
46
|
+
extra&.
|
47
|
+
response_object&.
|
48
|
+
attributes
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
user = Models::User.where(
|
51
|
+
email: attributes[:email],
|
52
|
+
idp_id: attributes[:id],
|
53
|
+
).first_or_create! do |new_user|
|
54
|
+
new_user.enterprise_account_id = provider.enterprise_account_id
|
55
|
+
new_user.identity_provider_id = provider.id
|
56
|
+
end
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
authorization_code = user.authorization_codes.create!(
|
59
|
+
oauth_client: oauth_client,
|
60
|
+
redirect_uri: redirect_uri,
|
61
|
+
)
|
60
62
|
|
61
|
-
|
63
|
+
redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{session[:oauth_state]}")
|
64
|
+
end
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|