osso 0.0.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.buildkite/hooks/environment +9 -0
  4. data/.buildkite/hooks/pre-command +7 -0
  5. data/.buildkite/pipeline.yml +3 -0
  6. data/.buildkite/template.yml +5 -0
  7. data/.gitignore +9 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +82 -0
  10. data/CODE_OF_CONDUCT.md +130 -0
  11. data/Gemfile +18 -0
  12. data/Gemfile.lock +174 -0
  13. data/LICENSE +111 -0
  14. data/README.md +2 -0
  15. data/Rakefile +14 -0
  16. data/bin/console +7 -0
  17. data/bin/setup +8 -0
  18. data/config/database.yml +14 -0
  19. data/lib/.DS_Store +0 -0
  20. data/lib/osso/Rakefile +13 -0
  21. data/lib/osso/db/migrate/20190909230109_enable_uuid.rb +7 -0
  22. data/lib/osso/db/migrate/20200328135750_create_users.rb +12 -0
  23. data/lib/osso/db/migrate/20200328143303_create_oauth_tables.rb +57 -0
  24. data/lib/osso/db/migrate/20200411144528_create_saml_providers.rb +13 -0
  25. data/lib/osso/db/migrate/20200411184535_add_provider_id_to_users.rb +7 -0
  26. data/lib/osso/db/migrate/20200411192645_create_enterprise_accounts.rb +15 -0
  27. data/lib/osso/db/migrate/20200413132407_add_oauth_clients.rb +13 -0
  28. data/lib/osso/db/migrate/20200413142511_create_authorization_codes.rb +15 -0
  29. data/lib/osso/db/migrate/20200413153029_add_oauth_client_reference_to_saml_providers.rb +5 -0
  30. data/lib/osso/db/migrate/20200413163451_create_access_tokens.rb +13 -0
  31. data/lib/osso/db/migrate/20200501203026_drop_null_constraints_from_saml_provider.rb +7 -0
  32. data/lib/osso/db/migrate/20200501204047_drop_acs_url.rb +5 -0
  33. data/lib/osso/db/migrate/20200502120616_create_redirect_uris_and_drop_from_oauth_clients.rb +13 -0
  34. data/lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_account.rb +5 -0
  35. data/lib/osso/db/migrate/20200601131227_drop_null_constraint_from_saml_providers_provider.rb +7 -0
  36. data/lib/osso/db/schema.rb +132 -0
  37. data/lib/osso/helpers/auth.rb +67 -0
  38. data/lib/osso/helpers/helpers.rb +6 -0
  39. data/lib/osso/lib/app_config.rb +20 -0
  40. data/lib/osso/lib/oauth2_token.rb +38 -0
  41. data/lib/osso/models/access_token.rb +29 -0
  42. data/lib/osso/models/authorization_code.rb +14 -0
  43. data/lib/osso/models/enterprise_account.rb +28 -0
  44. data/lib/osso/models/models.rb +16 -0
  45. data/lib/osso/models/oauth_client.rb +32 -0
  46. data/lib/osso/models/redirect_uri.rb +20 -0
  47. data/lib/osso/models/saml_provider.rb +52 -0
  48. data/lib/osso/models/saml_providers/azure_saml_provider.rb +22 -0
  49. data/lib/osso/models/saml_providers/okta_saml_provider.rb +23 -0
  50. data/lib/osso/models/user.rb +24 -0
  51. data/lib/osso/rake.rb +4 -0
  52. data/lib/osso/routes/admin.rb +42 -0
  53. data/lib/osso/routes/auth.rb +64 -0
  54. data/lib/osso/routes/oauth.rb +57 -0
  55. data/lib/osso/routes/routes.rb +10 -0
  56. data/lib/osso/routes/views/error.erb +1 -0
  57. data/lib/osso/routes/views/multiple_providers.erb +1 -0
  58. data/lib/osso/version.rb +5 -0
  59. data/lib/osso.rb +9 -0
  60. data/lib/tasks/bootstrap.rake +17 -0
  61. data/osso-rb.gemspec +40 -0
  62. data/spec/factories/authorization_code.rb +10 -0
  63. data/spec/factories/enterprise_account.rb +45 -0
  64. data/spec/factories/oauth_client.rb +12 -0
  65. data/spec/factories/redirect_uri.rb +14 -0
  66. data/spec/factories/saml_providers.rb +46 -0
  67. data/spec/factories/user.rb +18 -0
  68. data/spec/models/azure_saml_provider_spec.rb +19 -0
  69. data/spec/models/okta_saml_provider_spec.rb +20 -0
  70. data/spec/models/saml_provider_spec.rb +31 -0
  71. data/spec/routes/admin_spec.rb +57 -0
  72. data/spec/routes/app_spec.rb +6 -0
  73. data/spec/routes/auth_spec.rb +112 -0
  74. data/spec/routes/oauth_spec.rb +134 -0
  75. data/spec/spec_helper.rb +65 -0
  76. data/spec/support/vcr_cassettes/okta_saml_callback.yml +59 -0
  77. metadata +302 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Models
5
+ # Subclass for Azure / ADFS IDP instances
6
+ class AzureSamlProvider < Models::SamlProvider
7
+ def name
8
+ 'Azure'
9
+ end
10
+
11
+ def saml_options
12
+ attributes.slice(
13
+ 'domain',
14
+ 'idp_cert',
15
+ 'idp_sso_target_url',
16
+ ).merge(
17
+ issuer: "id:#{id}",
18
+ ).symbolize_keys
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Models
5
+ # Subclass for Okta IDP instances
6
+ class OktaSamlProvider < Models::SamlProvider
7
+ def name
8
+ 'Okta'
9
+ end
10
+
11
+ def saml_options
12
+ attributes.slice(
13
+ 'domain',
14
+ 'idp_cert',
15
+ 'idp_sso_target_url',
16
+ ).merge(
17
+ issuer: id,
18
+ name_identifier_format: NAME_FORMAT,
19
+ ).symbolize_keys
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Models
5
+ class User < ActiveRecord::Base
6
+ belongs_to :enterprise_account
7
+ belongs_to :saml_provider
8
+ has_many :authorization_codes, dependent: :delete_all
9
+ has_many :access_tokens, dependent: :delete_all
10
+
11
+ def oauth_client
12
+ saml_provider.oauth_client
13
+ end
14
+
15
+ def as_json(*)
16
+ {
17
+ email: email,
18
+ id: id,
19
+ idp: saml_provider.name,
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
data/lib/osso/rake.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ load 'active_record/railties/databases.rake'
4
+ require "sinatra/activerecord/rake/activerecord_#{ActiveRecord::VERSION::MAJOR}"
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt'
4
+
5
+ module Osso
6
+ class Admin < Sinatra::Base
7
+ include AppConfig
8
+ helpers Helpers::Auth
9
+
10
+ before do
11
+ chomp_token
12
+ end
13
+
14
+ get '/' do
15
+ admin_protected!
16
+
17
+ erb :'public/index'
18
+ end
19
+
20
+ get '/enterprise' do
21
+ admin_protected!
22
+
23
+ erb :admin
24
+ end
25
+
26
+ get '/enterprise/:domain' do
27
+ enterprise_protected!(params[:domain])
28
+
29
+ @enterprise = Models::EnterpriseAccount.where(
30
+ domain: params[:domain],
31
+ ).first_or_create
32
+
33
+ erb :admin
34
+ end
35
+
36
+ get '/config' do
37
+ admin_protected!
38
+
39
+ erb :admin
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'omniauth'
5
+ require 'omniauth-multi-provider'
6
+ require 'omniauth-saml'
7
+
8
+ module Osso
9
+ class Auth < Sinatra::Base
10
+ include AppConfig
11
+
12
+ UUID_REGEXP =
13
+ /[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/.
14
+ freeze
15
+
16
+ def self.internal_redirect?(env)
17
+ env['HTTP_REFERER']&.match(env['SERVER_NAME'])
18
+ end
19
+
20
+ use OmniAuth::Builder do
21
+ OmniAuth::MultiProvider.register(
22
+ self,
23
+ provider_name: 'saml',
24
+ identity_provider_id_regex: UUID_REGEXP,
25
+ path_prefix: '/saml',
26
+ callback_suffix: 'callback',
27
+ ) do |saml_provider_id, _env|
28
+ provider = Models::SamlProvider.find(saml_provider_id)
29
+ provider.saml_options
30
+ end
31
+ end
32
+
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
42
+
43
+ attributes = env['omniauth.auth']&.
44
+ extra&.
45
+ response_object&.
46
+ attributes
47
+
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
55
+
56
+ authorization_code = user.authorization_codes.create!(
57
+ oauth_client: oauth_client,
58
+ redirect_uri: redirect_uri,
59
+ )
60
+
61
+ redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{session[:oauth_state]}")
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/oauth2'
4
+
5
+ module Osso
6
+ class Oauth < Sinatra::Base
7
+ include AppConfig
8
+ # Send your users here in order to being an authentication
9
+ # flow. This flow follows the authorization grant oauth
10
+ # spec with one exception - you must also pass the domain
11
+ # of the user who wants to sign in.
12
+ get '/authorize' do
13
+ @enterprise = Models::EnterpriseAccount.
14
+ includes(:saml_providers).
15
+ find_by!(domain: params[:domain])
16
+
17
+ Rack::OAuth2::Server::Authorize.new do |req, _res|
18
+ client = Models::OauthClient.find_by!(identifier: req.client_id)
19
+ req.verify_redirect_uri!(client.redirect_uri_values)
20
+ end.call(env)
21
+
22
+ if @enterprise.single_provider?
23
+ session[:oauth_state] = params[:state]
24
+ redirect "/auth/saml/#{@enterprise.provider.id}"
25
+ end
26
+
27
+ erb :multiple_providers
28
+
29
+ rescue Rack::OAuth2::Server::Authorize::BadRequest => e
30
+ @error = e
31
+ return erb :error
32
+ end
33
+
34
+ # Exchange an authorization code token for an access token.
35
+ # In addition to the token, you must include all paramaters
36
+ # required by Oauth spec: redirect_uri, client ID, and client secret
37
+ post '/token' do
38
+ Rack::OAuth2::Server::Token.new do |req, res|
39
+ code = Models::AuthorizationCode.
40
+ find_by_token!(params[:code])
41
+ client = Models::OauthClient.find_by!(identifier: req.client_id)
42
+ req.invalid_client! if client.secret != req.client_secret
43
+ req.invalid_grant! if code.redirect_uri != req.redirect_uri
44
+ res.access_token = code.access_token.to_bearer_token
45
+ end.call(env)
46
+ end
47
+
48
+ # Use the access token to request a user profile
49
+ get '/me' do
50
+ json Models::AccessToken.
51
+ includes(:user).
52
+ valid.
53
+ find_by_token!(params[:access_token]).
54
+ user
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/contrib'
4
+ require 'sinatra/base'
5
+ require 'sinatra/contrib'
6
+ require 'sinatra/json'
7
+
8
+ require_relative 'admin'
9
+ require_relative 'auth'
10
+ require_relative 'oauth'
@@ -0,0 +1 @@
1
+ <%= @error %>
@@ -0,0 +1 @@
1
+ multiple providers
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ VERSION = '0.0.2.8'
5
+ end
data/lib/osso.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ require_relative 'osso/helpers/helpers'
5
+ require_relative 'osso/lib/app_config'
6
+ require_relative 'osso/lib/oauth2_token'
7
+ require_relative 'osso/models/models'
8
+ require_relative 'osso/routes/routes'
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra/activerecord'
4
+ require 'sinatra/activerecord/rake'
5
+ require 'osso'
6
+ require 'dotenv/tasks'
7
+
8
+ namespace :osso do
9
+ desc 'Bootstrap Osso data for a deployment'
10
+ task bootstrap: :dotenv do
11
+ %w[Production Staging Development].each do |environement|
12
+ Osso::Models::OauthClient.create!(
13
+ name: environement,
14
+ )
15
+ end
16
+ end
17
+ end
data/osso-rb.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/osso/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'osso'
7
+ spec.version = Osso::VERSION
8
+ spec.authors = ['Sam Bauch']
9
+ spec.email = ['sbauch@gmail.com']
10
+
11
+ spec.summary = 'Main functionality for Osso'
12
+ spec.description = 'This gem includes the main functionality for Osso apps,'
13
+ spec.homepage = 'https://github.com/enterprise-oss/osso-rb'
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+ spec.license = 'MIT'
16
+
17
+ spec.add_runtime_dependency 'activesupport', '>= 6.0.3.2'
18
+ spec.add_runtime_dependency 'jwt'
19
+ spec.add_runtime_dependency 'omniauth-multi-provider'
20
+ spec.add_runtime_dependency 'omniauth-saml'
21
+ spec.add_runtime_dependency 'rack', '>= 2.1.4'
22
+ spec.add_runtime_dependency 'rack-contrib'
23
+ spec.add_runtime_dependency 'rack-oauth2'
24
+ spec.add_runtime_dependency 'rake'
25
+ spec.add_runtime_dependency 'sinatra'
26
+ spec.add_runtime_dependency 'sinatra-activerecord'
27
+ spec.add_runtime_dependency 'sinatra-contrib'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 2.1'
30
+ spec.add_development_dependency 'pry'
31
+
32
+ # Specify which files should be added to the gem when it is released.
33
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
34
+ spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
35
+ spec.files = `git ls-files`.split("\n")
36
+ spec.test_files = `git ls-files -- {spec}/*`.split("\n")
37
+ spec.bindir = 'bin'
38
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
39
+ spec.require_paths = ['lib']
40
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :authorization_code, class: Osso::Models::AuthorizationCode do
5
+ id { SecureRandom.uuid }
6
+ redirect_uri { Faker::Internet.url(path: '/saml-box/callback') }
7
+ user
8
+ oauth_client
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :enterprise_account, class: Osso::Models::EnterpriseAccount do
5
+ id { SecureRandom.uuid }
6
+ domain { Faker::Internet.domain_name }
7
+ oauth_client
8
+ end
9
+
10
+ factory :enterprise_with_okta, parent: :enterprise_account do
11
+ after :create do |enterprise|
12
+ create(
13
+ :okta_saml_provider,
14
+ domain: enterprise.domain,
15
+ enterprise_account_id: enterprise.id,
16
+ )
17
+ end
18
+ end
19
+
20
+ factory :enterprise_with_azure, parent: :enterprise_account do
21
+ after :create do |enterprise|
22
+ create(
23
+ :azure_saml_provider,
24
+ domain: enterprise.domain,
25
+ enterprise_account_id: enterprise.id,
26
+ )
27
+ end
28
+ end
29
+
30
+ factory :enterprise_with_multiple_providers, parent: :enterprise_account do
31
+ after :create do |enterprise|
32
+ create(
33
+ :okta_saml_provider,
34
+ domain: enterprise.domain,
35
+ enterprise_account_id: enterprise.id,
36
+ )
37
+
38
+ create(
39
+ :azure_saml_provider,
40
+ domain: enterprise.domain,
41
+ enterprise_account_id: enterprise.id,
42
+ )
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :oauth_client, class: Osso::Models::OauthClient do
5
+ id { SecureRandom.uuid }
6
+ name { Faker::Internet.domain_name }
7
+ after(:create) do |client|
8
+ create(:primary_redirect_uri, oauth_client: client)
9
+ create(:redirect_uri, oauth_client: client)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :redirect_uri, class: Osso::Models::RedirectUri do
5
+ id { SecureRandom.uuid }
6
+ uri { Faker::Internet.url }
7
+ primary { false }
8
+ oauth_client
9
+ end
10
+
11
+ factory :primary_redirect_uri, parent: :redirect_uri do
12
+ primary { true }
13
+ end
14
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :saml_provider, class: Osso::Models::SamlProvider do
5
+ id { SecureRandom.uuid }
6
+ domain { Faker::Internet.domain_name }
7
+ oauth_client
8
+ idp_cert do
9
+ <<~CERT
10
+ -----BEGIN CERTIFICATE-----
11
+ MIIDpDCCAoygAwIBAgIGAXEiD4LlMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG
12
+ A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
13
+ MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xNjIwMjQxHDAaBgkqhkiG9w0BCQEW
14
+ DWluZm9Ab2t0YS5jb20wHhcNMjAwMzI4MTY1MTU0WhcNMzAwMzI4MTY1MjU0WjCBkjELMAkGA1UE
15
+ BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
16
+ BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTYyMDI0MRwwGgYJ
17
+ KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
18
+ wsnP4UTfv3bxR5Jh0at51Dqjj+fKxFznzFW3XA5NbF2SlRLjeYcvj3+47TC0eP6xOsLWfnvdnx4v
19
+ dd9Ufn7jDCo5pL3JykMVEh2I0szF3RLC+a532ArcwgU9Px48+rWVwPkASS7l4NHAM4+gOBHJMQt2
20
+ AMohPT0kU41P8BEPzfwhNyiEXR66JNZIJUE8fM3Vpgnxm/VSwYzJf0NfOyfxv8JczF0zkDbpE7Tk
21
+ 3Ww/PFFLoMxWzanWGJQ+blnhv6UV6H4fcfAbcwAplOdIVHjS2ghYBvYNGahuFxjia0+6csyZGrt8
22
+ H4XmR5Dr+jXY5K1b1VOA0k19/FCnHHN/smn25wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBgD9NE
23
+ 4OCuR1+vucV8S1T6XXIL2hB7bXBAZEVHZ1aErRzktgXAMgVwG267vIkD5VOXBiTy9yNU5LK6G3k2
24
+ zewU190sL1dMfyPnoVZyn94nvwe9A+on0tmZdmk00xirKk3FJdacnZNE9Dl/afIrcNf6xAm0WsU9
25
+ kbMiRwwvjO4TAiygDQzbrRC8ZfmT3hpBa3aTUzAccrvEQcgarLk4r7UjXP7a2mCN3UIIh+snN2Ms
26
+ vXHL0r6fM3xbniz+5lleWtPFw73yySBc8znkWZ4Tn8Lh0r6o5nCRYbr2REUB7ZIfiIyBbZxIp4kv
27
+ a+habbnQDFiNVzEd8OPXHh4EqLxOPDRW
28
+ -----END CERTIFICATE-----
29
+ CERT
30
+ end
31
+
32
+ factory :okta_saml_provider, parent: :saml_provider, class: Osso::Models::OktaSamlProvider do
33
+ provider { 'Osso::Models::OktaSamlProvider' }
34
+ idp_sso_target_url do
35
+ 'https://dev-162024.okta.com/app/vcardmedev162024_rubydemo2_1/exk51326b3U1941Hf4x6/sso/saml'
36
+ end
37
+ end
38
+
39
+ factory :azure_saml_provider, parent: :saml_provider, class: Osso::Models::AzureSamlProvider do
40
+ provider { 'Osso::Models::AzureSamlProvider' }
41
+ idp_sso_target_url do
42
+ 'https://login.microsoftonline.com/0af6c610-c40c-4683-9ea4-f25e509b8172/saml2'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :user, class: Osso::Models::User do
5
+ id { SecureRandom.uuid }
6
+ email { Faker::Internet.email }
7
+ idp_id { SecureRandom.hex(32) }
8
+ saml_provider { create(:okta_saml_provider) }
9
+ enterprise_account
10
+ after(:create) do |user|
11
+ create(
12
+ :authorization_code,
13
+ user: user,
14
+ redirect_uri: user.oauth_client.redirect_uri_values.sample,
15
+ )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Osso::Models::AzureSamlProvider do
6
+ subject { create(:azure_saml_provider) }
7
+
8
+ describe '#saml_options' do
9
+ it 'returns the required args' do
10
+ expect(subject.saml_options).
11
+ to match(
12
+ domain: subject.domain,
13
+ idp_cert: subject.idp_cert,
14
+ idp_sso_target_url: subject.idp_sso_target_url,
15
+ issuer: "id:#{subject.id}",
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Osso::Models::OktaSamlProvider do
6
+ subject { create(:okta_saml_provider) }
7
+
8
+ describe '#saml_options' do
9
+ it 'returns the required args' do
10
+ expect(subject.saml_options).
11
+ to match(
12
+ domain: subject.domain,
13
+ idp_cert: subject.idp_cert,
14
+ idp_sso_target_url: subject.idp_sso_target_url,
15
+ issuer: subject.id,
16
+ name_identifier_format: described_class::NAME_FORMAT,
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Osso::Models::SamlProvider do
6
+ subject { create(:okta_saml_provider) }
7
+
8
+ describe '.create' do
9
+ it 'creates an enterprise account' do
10
+ domain = Faker::Internet.domain_name
11
+
12
+ provider = described_class.create(
13
+ domain: domain,
14
+ provider: 'Osso::Models::OktaSamlProvider',
15
+ )
16
+
17
+ expect(provider.enterprise_account).to be_a(Osso::Models::EnterpriseAccount)
18
+ expect(provider.enterprise_account.domain).to eq(domain)
19
+ end
20
+ end
21
+
22
+ describe '#assertion_consumer_service_url' do
23
+ it 'returns the expected URI' do
24
+ ENV['BASE_URL'] = 'https://example.com'
25
+
26
+ expect(subject.assertion_consumer_service_url).to eq(
27
+ "https://example.com/auth/saml/#{subject.id}/callback",
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Osso::Admin do
6
+ let(:jwt_url) { 'https://foo.com/jwt' }
7
+ let(:jwt_hmac_secret) { SecureRandom.hex(32) }
8
+
9
+ before do
10
+ ENV['JWT_URL'] = jwt_url
11
+ ENV['JWT_HMAC_SECRET'] = jwt_hmac_secret
12
+ end
13
+
14
+ describe 'get /admin' do
15
+ it 'redirects to JWT_URL without a session or token' do
16
+ get('/admin')
17
+
18
+ expect(last_response).to be_redirect
19
+ follow_redirect!
20
+ expect(last_request.url).to eq(jwt_url)
21
+ end
22
+
23
+ it 'redirects to JWT_URL with an invalid token' do
24
+ get('/admin', token: SecureRandom.hex(32))
25
+
26
+ expect(last_response).to be_redirect
27
+ follow_redirect!
28
+ expect(last_request.url).to eq(jwt_url)
29
+ end
30
+
31
+ it 'chomps the token and redirects to request path with valid token' do
32
+ token = JWT.encode(
33
+ { email: 'admin@saas.com', scope: 'admin' },
34
+ jwt_hmac_secret,
35
+ 'HS256',
36
+ )
37
+
38
+ get('/admin', { admin_token: token })
39
+
40
+ expect(last_response).to be_redirect
41
+ follow_redirect!
42
+ expect(last_request.url).to match('/admin')
43
+ end
44
+
45
+ it 'renders the admin page for a valid session token' do
46
+ token = JWT.encode(
47
+ { email: 'admin@saas.com', scope: 'admin' },
48
+ jwt_hmac_secret,
49
+ 'HS256',
50
+ )
51
+
52
+ get('/admin', {}, 'rack.session' => { admin_token: token })
53
+
54
+ expect(last_response).to be_ok
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'App' do
6
+ end