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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +4 -1
  3. data/.rubocop.yml +1 -2
  4. data/Gemfile.lock +3 -1
  5. data/bin/console +4 -3
  6. data/config/database.yml +2 -2
  7. data/db/schema.rb +133 -1
  8. data/lib/osso.rb +2 -0
  9. data/lib/osso/db/migrate/20200328143305_create_identity_providers.rb +12 -0
  10. data/lib/osso/db/migrate/20200411184535_add_provider_id_to_users.rb +2 -2
  11. data/lib/osso/db/migrate/20200411192645_create_enterprise_accounts.rb +1 -1
  12. data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_accounts_and_identity_providers.rb +6 -0
  13. data/lib/osso/db/migrate/20200714223226_add_identity_provider_service_enum.rb +17 -0
  14. data/lib/osso/db/migrate/20200715154211_rename_idp_fields_on_identity_provider_to_sso.rb +6 -0
  15. data/lib/osso/db/migrate/20200715205801_add_name_to_enterprise_account.rb +5 -0
  16. data/lib/osso/graphql/mutation.rb +10 -5
  17. data/lib/osso/graphql/mutations.rb +5 -2
  18. data/lib/osso/graphql/mutations/base_mutation.rb +35 -14
  19. data/lib/osso/graphql/mutations/configure_identity_provider.rb +31 -21
  20. data/lib/osso/graphql/mutations/create_enterprise_account.rb +25 -0
  21. data/lib/osso/graphql/mutations/create_identity_provider.rb +22 -16
  22. data/lib/osso/graphql/mutations/set_identity_provider.rb +27 -0
  23. data/lib/osso/graphql/query.rb +19 -22
  24. data/lib/osso/graphql/resolvers.rb +5 -1
  25. data/lib/osso/graphql/resolvers/enterprise_account.rb +16 -12
  26. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +10 -6
  27. data/lib/osso/graphql/resolvers/oauth_clients.rb +9 -5
  28. data/lib/osso/graphql/schema.rb +27 -19
  29. data/lib/osso/graphql/types.rb +4 -1
  30. data/lib/osso/graphql/types/base_enum.rb +6 -2
  31. data/lib/osso/graphql/types/base_input_object.rb +10 -0
  32. data/lib/osso/graphql/types/base_object.rb +6 -2
  33. data/lib/osso/graphql/types/enterprise_account.rb +22 -18
  34. data/lib/osso/graphql/types/identity_provider.rb +26 -25
  35. data/lib/osso/graphql/types/identity_provider_service.rb +8 -4
  36. data/lib/osso/graphql/types/oauth_client.rb +13 -9
  37. data/lib/osso/graphql/types/user.rb +10 -5
  38. data/lib/osso/helpers/auth.rb +5 -3
  39. data/lib/osso/lib/app_config.rb +1 -1
  40. data/lib/osso/lib/route_map.rb +28 -0
  41. data/lib/osso/models/enterprise_account.rb +4 -4
  42. data/lib/osso/models/identity_provider.rb +48 -0
  43. data/lib/osso/models/models.rb +1 -1
  44. data/lib/osso/models/oauth_client.rb +2 -2
  45. data/lib/osso/models/saml_provider.rb +13 -16
  46. data/lib/osso/models/saml_providers/azure_saml_provider.rb +2 -2
  47. data/lib/osso/models/saml_providers/okta_saml_provider.rb +1 -1
  48. data/lib/osso/models/user.rb +3 -3
  49. data/lib/osso/routes/admin.rb +18 -15
  50. data/lib/osso/routes/auth.rb +30 -27
  51. data/lib/osso/routes/oauth.rb +50 -45
  52. data/lib/osso/version.rb +1 -1
  53. data/osso-rb.gemspec +3 -3
  54. data/spec/factories/enterprise_account.rb +5 -4
  55. data/spec/factories/identity_providers.rb +49 -0
  56. data/spec/factories/user.rb +1 -1
  57. data/spec/graphql/mutations/configure_identity_provider_spec.rb +75 -0
  58. data/spec/graphql/mutations/create_enterprise_account_spec.rb +68 -0
  59. data/spec/graphql/mutations/create_identity_provider_spec.rb +104 -0
  60. data/spec/graphql/query/enterprise_account_spec.rb +68 -0
  61. data/spec/graphql/query/enterprise_accounts_spec.rb +44 -0
  62. data/spec/graphql/query/identity_provider_spec.rb +65 -0
  63. data/spec/graphql/query/oauth_clients_account_spec.rb +48 -0
  64. data/spec/models/azure_saml_provider_spec.rb +14 -14
  65. data/spec/models/identity_provider_spec.rb +17 -0
  66. data/spec/models/okta_saml_provider_spec.rb +15 -15
  67. data/spec/routes/admin_spec.rb +2 -0
  68. data/spec/routes/auth_spec.rb +9 -9
  69. data/spec/routes/oauth_spec.rb +1 -1
  70. data/spec/spec_helper.rb +4 -5
  71. data/spec/support/spec_app.rb +9 -0
  72. data/spec/support/views/admin.erb +5 -0
  73. metadata +37 -12
  74. data/lib/osso/db/migrate/20200411144528_create_saml_providers.rb +0 -13
  75. data/lib/osso/db/migrate/20200413153029_add_oauth_client_reference_to_saml_providers.rb +0 -5
  76. data/lib/osso/db/migrate/20200501203026_drop_null_constraints_from_saml_provider.rb +0 -7
  77. data/lib/osso/db/migrate/20200501204047_drop_acs_url.rb +0 -5
  78. data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_account.rb +0 -5
  79. data/lib/osso/db/migrate/20200601131227_drop_null_constraint_from_saml_providers_provider.rb +0 -7
  80. data/lib/osso/db/schema.rb +0 -132
  81. data/lib/osso/graphql/mutations/set_saml_provider.rb +0 -23
  82. data/spec/factories/saml_providers.rb +0 -46
  83. data/spec/models/saml_provider_spec.rb +0 -31
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Types
4
- class IdentityProviderService < BaseEnum
5
- value('AZURE', 'Microsoft Azure Identity Provider', value: 'Osso::Models::AzureSamlProvider')
6
- value('OKTA', 'Okta Identity Provider', value: 'Osso::Models::OktaSamlProvider')
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 Types
6
- class OAuthClient < Types::BaseObject
7
- description 'An OAuth client used to consume Osso SAML users'
8
- implements GraphQL::Types::Relay::Node
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
- global_id_field :gid
11
- field :id, ID, null: false
12
- field :name, String, null: false
13
- field :client_id, String, null: false
14
- field :client_secret, String, null: false
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
- field :id, ID, null: false
10
- field :name, String, null: true
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
@@ -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?(domain)
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
@@ -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.fetch('SESSION_SECRET')
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 :saml_providers
13
+ has_many :identity_providers
14
14
 
15
15
  def single_provider?
16
- saml_providers.one?
16
+ identity_providers.one?
17
17
  end
18
18
 
19
19
  def provider
20
20
  return nil unless single_provider?
21
21
 
22
- saml_providers.first
22
+ identity_providers.first
23
23
  end
24
24
 
25
- alias saml_provider provider
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
@@ -12,5 +12,5 @@ require_relative 'authorization_code'
12
12
  require_relative 'enterprise_account'
13
13
  require_relative 'oauth_client'
14
14
  require_relative 'redirect_uri'
15
- require_relative 'saml_provider'
15
+ require_relative 'identity_provider'
16
16
  require_relative 'user'
@@ -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 :saml_providers
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 SamlProvider < ActiveRecord::Base
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
- raise(
17
- NoMethodError,
18
- '#name must be defined on each provider specific subclass',
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
- raise(
24
- NoMethodError,
25
- '#saml_options must be defined on each provider specific subclass',
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::SamlProvider
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
@@ -3,7 +3,7 @@
3
3
  module Osso
4
4
  module Models
5
5
  # Subclass for Okta IDP instances
6
- class OktaSamlProvider < Models::SamlProvider
6
+ class OktaSamlProvider < Models::IdentityProvider
7
7
  def name
8
8
  'Okta'
9
9
  end
@@ -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 :saml_provider
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
- saml_provider.oauth_client
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: saml_provider.name,
19
+ idp: identity_provider.name,
20
20
  }
21
21
  end
22
22
  end
@@ -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
- get '/' do
15
- admin_protected!
15
+ namespace '/admin' do
16
+ get '' do
17
+ admin_protected!
16
18
 
17
- erb :admin
18
- end
19
+ erb :admin
20
+ end
19
21
 
20
- get '/enterprise' do
21
- admin_protected!
22
+ get '/enterprise' do
23
+ admin_protected!
22
24
 
23
- erb :admin
24
- end
25
+ erb :admin
26
+ end
25
27
 
26
- get '/enterprise/:domain' do
27
- enterprise_protected!(params[:domain])
28
+ get '/enterprise/:domain' do
29
+ enterprise_protected!(params[:domain])
28
30
 
29
- erb :admin
30
- end
31
+ erb :admin
32
+ end
31
33
 
32
- get '/config' do
33
- admin_protected!
34
+ get '/config' do
35
+ admin_protected!
34
36
 
35
- erb :admin
37
+ erb :admin
38
+ end
36
39
  end
37
40
  end
38
41
  end
@@ -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 |saml_provider_id, _env|
28
- provider = Models::SamlProvider.find(saml_provider_id)
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
- # Enterprise users are sent here after authenticating against
34
- # their Identity Provider. We find or create a user record,
35
- # and then create an authorization code for that user. The user
36
- # is redirected back to your application with this code
37
- # as a URL query param, which you then exhange for an access token
38
- post '/saml/:id/callback' do
39
- provider = Models::SamlProvider.find(params[:id])
40
- oauth_client = provider.oauth_client
41
- redirect_uri = env['redirect_uri'] || oauth_client.default_redirect_uri.uri
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
- attributes = env['omniauth.auth']&.
44
- extra&.
45
- response_object&.
46
- attributes
45
+ attributes = env['omniauth.auth']&.
46
+ extra&.
47
+ response_object&.
48
+ attributes
47
49
 
48
- user = Models::User.where(
49
- email: attributes[:email],
50
- idp_id: attributes[:id],
51
- ).first_or_create! do |new_user|
52
- new_user.enterprise_account_id = provider.enterprise_account_id
53
- new_user.saml_provider_id = provider.id
54
- end
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
- authorization_code = user.authorization_codes.create!(
57
- oauth_client: oauth_client,
58
- redirect_uri: redirect_uri,
59
- )
58
+ authorization_code = user.authorization_codes.create!(
59
+ oauth_client: oauth_client,
60
+ redirect_uri: redirect_uri,
61
+ )
60
62
 
61
- redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{session[:oauth_state]}")
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