osso 0.0.3.16 → 0.0.3.21

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +9 -0
  3. data/.rubocop.yml +1 -0
  4. data/Gemfile +1 -0
  5. data/Gemfile.lock +9 -1
  6. data/README.md +3 -2
  7. data/db/schema.rb +9 -1
  8. data/lib/osso/db/migrate/20200826201852_create_app_config.rb +11 -0
  9. data/lib/osso/graphql/mutation.rb +7 -0
  10. data/lib/osso/graphql/mutations.rb +1 -0
  11. data/lib/osso/graphql/mutations/base_mutation.rb +18 -5
  12. data/lib/osso/graphql/mutations/configure_identity_provider.rb +8 -10
  13. data/lib/osso/graphql/mutations/create_enterprise_account.rb +7 -0
  14. data/lib/osso/graphql/mutations/create_identity_provider.rb +14 -5
  15. data/lib/osso/graphql/mutations/create_oauth_client.rb +1 -3
  16. data/lib/osso/graphql/mutations/delete_enterprise_account.rb +9 -11
  17. data/lib/osso/graphql/mutations/delete_oauth_client.rb +1 -3
  18. data/lib/osso/graphql/mutations/regenerate_oauth_credentials.rb +1 -3
  19. data/lib/osso/graphql/mutations/set_redirect_uris.rb +1 -3
  20. data/lib/osso/graphql/mutations/update_app_config.rb +29 -0
  21. data/lib/osso/graphql/query.rb +14 -0
  22. data/lib/osso/graphql/resolvers.rb +1 -0
  23. data/lib/osso/graphql/resolvers/base_resolver.rb +25 -0
  24. data/lib/osso/graphql/resolvers/enterprise_account.rb +1 -11
  25. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +2 -2
  26. data/lib/osso/graphql/resolvers/oauth_clients.rb +2 -2
  27. data/lib/osso/graphql/types.rb +2 -1
  28. data/lib/osso/graphql/types/admin_user.rb +22 -0
  29. data/lib/osso/graphql/types/app_config.rb +22 -0
  30. data/lib/osso/graphql/types/base_object.rb +22 -0
  31. data/lib/osso/graphql/types/enterprise_account.rb +0 -5
  32. data/lib/osso/graphql/types/identity_provider.rb +0 -6
  33. data/lib/osso/graphql/types/oauth_client.rb +2 -4
  34. data/lib/osso/graphql/types/redirect_uri.rb +2 -4
  35. data/lib/osso/helpers/auth.rb +40 -18
  36. data/lib/osso/lib/route_map.rb +2 -2
  37. data/lib/osso/models/app_config.rb +33 -0
  38. data/lib/osso/models/identity_provider.rb +5 -5
  39. data/lib/osso/models/models.rb +1 -0
  40. data/lib/osso/models/oauth_client.rb +3 -2
  41. data/lib/osso/routes/admin.rb +8 -2
  42. data/lib/osso/routes/auth.rb +7 -3
  43. data/lib/osso/routes/oauth.rb +21 -14
  44. data/lib/osso/version.rb +1 -1
  45. data/lib/tasks/bootstrap.rake +2 -0
  46. data/spec/graphql/mutations/configure_identity_provider_spec.rb +17 -4
  47. data/spec/graphql/mutations/create_enterprise_account_spec.rb +53 -4
  48. data/spec/graphql/mutations/create_identity_provider_spec.rb +18 -6
  49. data/spec/graphql/mutations/create_oauth_client_spec.rb +10 -3
  50. data/spec/graphql/mutations/delete_enterprise_account_spec.rb +18 -4
  51. data/spec/graphql/mutations/delete_oauth_client_spec.rb +8 -4
  52. data/spec/graphql/query/enterprise_account_spec.rb +21 -6
  53. data/spec/graphql/query/enterprise_accounts_spec.rb +4 -2
  54. data/spec/graphql/query/identity_provider_spec.rb +16 -6
  55. data/spec/graphql/query/oauth_clients_spec.rb +10 -7
  56. data/spec/helpers/auth_spec.rb +97 -0
  57. data/spec/routes/oauth_spec.rb +5 -2
  58. data/spec/spec_helper.rb +3 -0
  59. data/spec/support/views/error.erb +0 -0
  60. metadata +10 -3
  61. data/lib/osso/graphql/types/user.rb +0 -17
@@ -27,7 +27,11 @@ module Osso
27
27
  end
28
28
  end
29
29
 
30
- namespace '/auth' do
30
+ namespace '/auth' do # rubocop:disable Metrics/BlockLength
31
+ get '/failure' do
32
+ @error = params[:message]
33
+ erb :error
34
+ end
31
35
  # Enterprise users are sent here after authenticating against
32
36
  # their Identity Provider. We find or create a user record,
33
37
  # and then create an authorization code for that user. The user
@@ -67,9 +71,9 @@ module Osso
67
71
  end
68
72
 
69
73
  def provider_state
70
- return 'IDP_INITIATED' if valid_idp_initiated_flow
74
+ return @provider_state = 'IDP_INITIATED' if valid_idp_initiated_flow
71
75
 
72
- session[:osso_oauth_state]
76
+ session.delete(:osso_oauth_state)
73
77
  end
74
78
 
75
79
  def valid_idp_initiated_flow
@@ -7,37 +7,43 @@ module Osso
7
7
  include AppConfig
8
8
  register Sinatra::Namespace
9
9
 
10
- namespace '/oauth' do
10
+ namespace '/oauth' do # rubocop:disable Metrics/BlockLength
11
11
  # Send your users here in order to being an authentication
12
12
  # flow. This flow follows the authorization grant oauth
13
13
  # spec with one exception - you must also pass the domain
14
- # of the user who wants to sign in.
14
+ # of the user who wants to sign in. If the sign in request
15
+ # is valid, the user is redirected to their Identity Provider.
16
+ # Once they complete IdP login, they will be returned to the
17
+ # redirect_uri with an authorization code parameter.
15
18
  get '/authorize' do
16
- @enterprise = Models::EnterpriseAccount.
17
- includes(:identity_providers).
18
- find_by!(domain: params[:domain])
19
-
20
19
  Rack::OAuth2::Server::Authorize.new do |req, _res|
21
20
  client = Models::OauthClient.find_by!(identifier: req.client_id)
22
21
  session[:osso_oauth_redirect_uri] = req.verify_redirect_uri!(client.redirect_uri_values)
22
+ session[:osso_oauth_state] = params[:state]
23
23
  end.call(env)
24
24
 
25
- if @enterprise.single_provider?
26
- session[:osso_oauth_state] = params[:state]
27
- redirect "/auth/saml/#{@enterprise.provider.id}"
28
- end
25
+ enterprise = Models::EnterpriseAccount.
26
+ includes(:identity_providers).
27
+ find_by!(domain: params[:domain])
28
+
29
+ redirect "/auth/saml/#{enterprise.provider.id}" if enterprise.single_provider?
29
30
 
30
31
  # TODO: multiple provider support
31
32
  # erb :multiple_providers
32
33
 
33
34
  rescue Rack::OAuth2::Server::Authorize::BadRequest => e
34
35
  @error = e
35
- return erb :error
36
+ erb :error
37
+ rescue ActiveRecord::RecordNotFound => e
38
+ @error = e
39
+ @error = 'No OAuth Client exists for the provided client_id' if e.model == 'Osso::Models::OauthClient'
40
+ @error = "No Customer exists with the domain #{params[:domain]}" if e.model == 'Osso::Models::EnterpriseAccount'
41
+ erb :error
36
42
  end
37
43
 
38
44
  # Exchange an authorization code for an access token.
39
- # In addition to the authorization code, you must include all
40
- # paramaters required by OAuth spec: redirect_uri, client ID,
45
+ # In addition to the authorization code, you must include all
46
+ # paramaters required by OAuth spec: redirect_uri, client ID,
41
47
  # and client secret
42
48
  post '/token' do
43
49
  Rack::OAuth2::Server::Token.new do |req, res|
@@ -50,7 +56,8 @@ module Osso
50
56
  end.call(env)
51
57
  end
52
58
 
53
- # Use the access token to request a user profile
59
+ # Use the access token to request a profile for the user who
60
+ # just logged in. Access tokens are short-lived.
54
61
  get '/me' do
55
62
  json Models::AccessToken.
56
63
  includes(:user).
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.3.16'
4
+ VERSION = '0.0.3.21'
5
5
  end
@@ -12,5 +12,7 @@ namespace :osso do
12
12
  name: environement,
13
13
  )
14
14
  end
15
+
16
+ Osso::Models::AppConfig.create!
15
17
  end
16
18
  end
@@ -39,12 +39,15 @@ describe Osso::GraphQL::Schema do
39
39
  described_class.execute(
40
40
  mutation,
41
41
  variables: variables,
42
- context: { scope: current_scope },
42
+ context: current_context,
43
43
  )
44
44
  end
45
45
 
46
46
  describe 'for an admin user' do
47
- let(:current_scope) { :admin }
47
+ let(:current_context) do
48
+ { scope: 'admin' }
49
+ end
50
+
48
51
  it 'configures an identity provider' do
49
52
  expect(subject.dig('data', 'configureIdentityProvider', 'identityProvider', 'status')).
50
53
  to eq('Configured')
@@ -53,7 +56,12 @@ describe Osso::GraphQL::Schema do
53
56
 
54
57
  describe 'for an email scoped user' do
55
58
  let(:domain) { Faker::Internet.domain_name }
56
- let(:current_scope) { domain }
59
+ let(:current_context) do
60
+ {
61
+ scope: 'end-user',
62
+ email: "user@#{domain}",
63
+ }
64
+ end
57
65
  let(:enterprise_account) { create(:enterprise_account, domain: domain) }
58
66
  let(:identity_provider) { create(:identity_provider, enterprise_account: enterprise_account, domain: domain) }
59
67
 
@@ -65,7 +73,12 @@ describe Osso::GraphQL::Schema do
65
73
 
66
74
  describe 'for the wrong email scoped user' do
67
75
  let(:domain) { Faker::Internet.domain_name }
68
- let(:current_scope) { domain }
76
+ let(:current_context) do
77
+ {
78
+ scope: 'end-user',
79
+ email: "user@#{domain}",
80
+ }
81
+ end
69
82
 
70
83
  it 'does not configure an identity provider' do
71
84
  expect(subject.dig('errors')).to_not be_empty
@@ -5,6 +5,7 @@ require 'spec_helper'
5
5
  describe Osso::GraphQL::Schema do
6
6
  describe 'CreateIdentityProvider' do
7
7
  let(:domain) { Faker::Internet.domain_name }
8
+ let!(:oauth_client) { create(:oauth_client) }
8
9
  let(:variables) do
9
10
  {
10
11
  input: {
@@ -33,30 +34,78 @@ describe Osso::GraphQL::Schema do
33
34
  described_class.execute(
34
35
  mutation,
35
36
  variables: variables,
36
- context: { scope: current_scope },
37
+ context: current_context,
37
38
  )
38
39
  end
39
40
 
40
41
  describe 'for an admin user' do
41
- let(:current_scope) { :admin }
42
+ let(:current_context) do
43
+ { scope: 'admin' }
44
+ end
45
+ let(:variables) do
46
+ {
47
+ input: {
48
+ name: Faker::Company.name,
49
+ domain: domain,
50
+ oauthClientId: oauth_client.id,
51
+ },
52
+ }
53
+ end
54
+
42
55
  it 'creates an Enterprise Account' do
43
56
  expect { subject }.to change { Osso::Models::EnterpriseAccount.count }.by(1)
44
57
  expect(subject.dig('data', 'createEnterpriseAccount', 'enterpriseAccount', 'domain')).
45
58
  to eq(domain)
46
59
  end
60
+
61
+ it 'attaches the Enterprise Account to the correct OAuth Client' do
62
+ expect { subject }.to change { oauth_client.enterprise_accounts.count }.by(1)
63
+ end
64
+ end
65
+
66
+ describe 'for an internal scoped user' do
67
+ let(:current_context) do
68
+ {
69
+ scope: 'internal',
70
+ email: 'user@saasco.com',
71
+ oauth_client_id: oauth_client.identifier,
72
+ }
73
+ end
74
+
75
+ it 'creates an Enterprise Account' do
76
+ expect { subject }.to change { Osso::Models::EnterpriseAccount.count }.by(1)
77
+ expect(subject.dig('data', 'createEnterpriseAccount', 'enterpriseAccount', 'domain')).
78
+ to eq(domain)
79
+ end
80
+
81
+ it 'attaches the Enterprise Account to the correct OAuth Client' do
82
+ expect { subject }.to change { oauth_client.enterprise_accounts.count }.by(1)
83
+ end
47
84
  end
48
85
 
49
86
  describe 'for an email scoped user' do
50
- let(:current_scope) { domain }
87
+ let(:current_context) do
88
+ {
89
+ scope: 'end-user',
90
+ email: "user@#{domain}",
91
+ oauth_client_id: oauth_client.identifier,
92
+ }
93
+ end
51
94
 
52
95
  it 'creates an Enterprise Account' do
53
96
  expect { subject }.to change { Osso::Models::EnterpriseAccount.count }.by(1)
54
97
  expect(subject.dig('data', 'createEnterpriseAccount', 'enterpriseAccount', 'domain')).
55
98
  to eq(domain)
56
99
  end
100
+
101
+ it 'attaches the Enterprise Account to the correct OAuth Client' do
102
+ expect { subject }.to change { oauth_client.enterprise_accounts.count }.by(1)
103
+ end
57
104
  end
58
105
  describe 'for the wrong email scoped user' do
59
- let(:current_scope) { 'foo.com' }
106
+ let(:current_context) do
107
+ { scope: 'end-user', email: 'user@foo.com' }
108
+ end
60
109
 
61
110
  it 'does not create an Enterprise Account' do
62
111
  expect { subject }.to_not(change { Osso::Models::EnterpriseAccount.count })
@@ -25,12 +25,14 @@ describe Osso::GraphQL::Schema do
25
25
  described_class.execute(
26
26
  mutation,
27
27
  variables: variables,
28
- context: { scope: current_scope },
28
+ context: current_context,
29
29
  )
30
30
  end
31
31
 
32
32
  describe 'for an admin user' do
33
- let(:current_scope) { :admin }
33
+ let(:current_context) do
34
+ { scope: 'admin' }
35
+ end
34
36
  describe 'without a service' do
35
37
  let(:variables) { { input: { enterpriseAccountId: enterprise_account.id } } }
36
38
 
@@ -54,7 +56,12 @@ describe Osso::GraphQL::Schema do
54
56
 
55
57
  describe 'for an email scoped user' do
56
58
  let(:domain) { Faker::Internet.domain_name }
57
- let(:current_scope) { domain }
59
+ let(:current_context) do
60
+ {
61
+ scope: 'end-user',
62
+ email: "user@#{domain}",
63
+ }
64
+ end
58
65
  let(:enterprise_account) { create(:enterprise_account, domain: domain) }
59
66
 
60
67
  describe 'without a service' do
@@ -80,12 +87,17 @@ describe Osso::GraphQL::Schema do
80
87
 
81
88
  describe 'for a wrong email scoped user' do
82
89
  let(:domain) { Faker::Internet.domain_name }
83
- let(:current_scope) { domain }
90
+ let(:current_context) do
91
+ {
92
+ scope: 'end-user',
93
+ email: "user@#{domain}",
94
+ }
95
+ end
84
96
  let(:enterprise_account) { create(:enterprise_account, domain: domain) }
85
97
  let(:target_account) { create(:enterprise_account) }
86
98
 
87
99
  describe 'without a service' do
88
- let(:variables) { { input: { enterpriseAccountId: target_account.id } } }
100
+ let(:variables) { { input: { enterpriseAccountId: target_account.id, domain: domain } } }
89
101
 
90
102
  it 'does not creates a identity provider' do
91
103
  expect { subject }.to_not(change { Osso::Models::IdentityProvider.count })
@@ -93,7 +105,7 @@ describe Osso::GraphQL::Schema do
93
105
  end
94
106
 
95
107
  describe 'with a service' do
96
- let(:variables) { { input: { enterpriseAccountId: target_account.id, service: 'OKTA' } } }
108
+ let(:variables) { { input: { enterpriseAccountId: target_account.id, service: 'OKTA', domain: domain } } }
97
109
 
98
110
  it 'does not creates a identity provider' do
99
111
  expect { subject }.to_not(change { Osso::Models::IdentityProvider.count })
@@ -31,12 +31,14 @@ describe Osso::GraphQL::Schema do
31
31
  described_class.execute(
32
32
  mutation,
33
33
  variables: variables,
34
- context: { scope: current_scope },
34
+ context: current_context,
35
35
  )
36
36
  end
37
37
 
38
38
  describe 'for an admin user' do
39
- let(:current_scope) { :admin }
39
+ let(:current_context) do
40
+ { scope: 'admin' }
41
+ end
40
42
  it 'creates an OauthClient' do
41
43
  expect { subject }.to change { Osso::Models::OauthClient.count }.by(1)
42
44
  expect(subject.dig('data', 'createOauthClient', 'oauthClient', 'clientId')).
@@ -45,7 +47,12 @@ describe Osso::GraphQL::Schema do
45
47
  end
46
48
 
47
49
  describe 'for an email scoped user' do
48
- let(:current_scope) { 'foo.com' }
50
+ let(:current_context) do
51
+ {
52
+ scope: 'end-user',
53
+ email: 'user@foo.com',
54
+ }
55
+ end
49
56
 
50
57
  it 'does not create an OauthClient Account' do
51
58
  expect { subject }.to_not(change { Osso::Models::OauthClient.count })
@@ -30,12 +30,15 @@ describe Osso::GraphQL::Schema do
30
30
  described_class.execute(
31
31
  mutation,
32
32
  variables: variables,
33
- context: { scope: current_scope },
33
+ context: current_context,
34
34
  )
35
35
  end
36
36
 
37
37
  describe 'for an admin user' do
38
- let(:current_scope) { :admin }
38
+ let(:current_context) do
39
+ { scope: 'admin' }
40
+ end
41
+
39
42
  it 'deletes an Enterprise Account' do
40
43
  expect { subject }.to change { Osso::Models::EnterpriseAccount.count }.by(-1)
41
44
  expect(subject.dig('data', 'createEnterpriseAccount', 'enterpriseAccount')).
@@ -44,7 +47,12 @@ describe Osso::GraphQL::Schema do
44
47
  end
45
48
 
46
49
  describe 'for an email scoped user' do
47
- let(:current_scope) { domain }
50
+ let(:current_context) do
51
+ {
52
+ scope: 'end-user',
53
+ email: "user@#{domain}",
54
+ }
55
+ end
48
56
 
49
57
  it 'deletes the Enterprise Account' do
50
58
  expect { subject }.to change { Osso::Models::EnterpriseAccount.count }.by(-1)
@@ -52,8 +60,14 @@ describe Osso::GraphQL::Schema do
52
60
  to be_nil
53
61
  end
54
62
  end
63
+
55
64
  describe 'for the wrong email scoped user' do
56
- let(:current_scope) { 'foo.com' }
65
+ let(:current_context) do
66
+ {
67
+ scope: 'end-user',
68
+ email: 'user@foo.com',
69
+ }
70
+ end
57
71
 
58
72
  it 'does not delete the Enterprise Account' do
59
73
  expect { subject }.to_not(change { Osso::Models::EnterpriseAccount.count })
@@ -29,21 +29,25 @@ describe Osso::GraphQL::Schema do
29
29
  described_class.execute(
30
30
  mutation,
31
31
  variables: variables,
32
- context: { scope: current_scope },
32
+ context: current_context,
33
33
  )
34
34
  end
35
35
 
36
36
  describe 'for an admin user' do
37
- let(:current_scope) { :admin }
37
+ let(:current_context) do
38
+ { scope: 'admin' }
39
+ end
38
40
  it 'deletes the OauthClient' do
39
41
  expect { subject }.to change { Osso::Models::OauthClient.count }.by(-1)
40
42
  end
41
43
  end
42
44
 
43
45
  describe 'for an email scoped user' do
44
- let(:current_scope) { 'foo.com' }
46
+ let(:current_context) do
47
+ { scope: 'end-user', email: 'user@foo.com' }
48
+ end
45
49
 
46
- it 'does not create an OauthClient Account' do
50
+ it 'does not deletes the OauthClient' do
47
51
  expect { subject }.to_not(change { Osso::Models::OauthClient.count })
48
52
  end
49
53
  end
@@ -37,12 +37,17 @@ describe Osso::GraphQL::Schema do
37
37
  described_class.execute(
38
38
  query,
39
39
  variables: variables,
40
- context: { scope: current_scope },
40
+ context: current_context,
41
41
  )
42
42
  end
43
43
 
44
44
  describe 'for an admin user' do
45
- let(:current_scope) { :admin }
45
+ let(:current_context) do
46
+ {
47
+ scope: 'admin',
48
+ }
49
+ end
50
+
46
51
  it 'returns Enterprise Account for domain' do
47
52
  expect(subject['errors']).to be_nil
48
53
  expect(subject.dig('data', 'enterpriseAccount', 'domain')).to eq(domain)
@@ -50,7 +55,12 @@ describe Osso::GraphQL::Schema do
50
55
  end
51
56
 
52
57
  describe 'for an email scoped user' do
53
- let(:current_scope) { domain }
58
+ let(:current_context) do
59
+ {
60
+ scope: 'end-user',
61
+ email: "user@#{domain}",
62
+ }
63
+ end
54
64
  it 'returns Enterprise Account for domain' do
55
65
  expect(subject['errors']).to be_nil
56
66
  expect(subject.dig('data', 'enterpriseAccount', 'domain')).to eq(domain)
@@ -58,9 +68,14 @@ describe Osso::GraphQL::Schema do
58
68
  end
59
69
 
60
70
  describe 'for the wrong email scoped user' do
61
- let(:current_scope) { 'bar.com' }
62
- it 'returns Enterprise Account for domain' do
63
- expect(subject['errors']).to be_nil
71
+ let(:current_context) do
72
+ {
73
+ scope: 'end-user',
74
+ email: 'foo@bar.com',
75
+ }
76
+ end
77
+ it 'does not return Enterprise Account for domain' do
78
+ expect(subject['errors']).to_not be_nil
64
79
  expect(subject.dig('data', 'enterpriseAccount')).to be_nil
65
80
  end
66
81
  end