osso 0.0.3.11 → 0.0.3.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffe9bf4e6f4de963af60c998d318a471e1ff3e33ef5a6c544c8923de762020bc
4
- data.tar.gz: a994f84634c584268e517688ec62b61315da4fa1ab2ea05787305b34deb964f5
3
+ metadata.gz: 6e33fd333f7c329404b9a9bdeb62551629b38a8b615b6aef556bc4b4c0ca2a03
4
+ data.tar.gz: e8c21ea78f2f33e5b6497c85148ff67221949ded6c34380dcb48a6eb450d6dc6
5
5
  SHA512:
6
- metadata.gz: 6c4e301385bb83a8ca5f7d66e4a3b3383c7ea3f58f395dc83e1cbadadb2a6896e1852ed787ae9a33a37a2871c3d5c28e5f33e072b7980ad904f958e95b4addb8
7
- data.tar.gz: 8529f13cc30d05946b7fa798b117483dd20b9415d27b6caccecfff228a666b73bfd1f36ceb7e19d7977803159a56aff0eca9075cb380fe7fde3ce560a1847217
6
+ metadata.gz: 76779ec670e1a6c12589a3b2a1f319e8855ba8770a83489787d56c43bb8a23aa9a75ee9b9864dfce76f54061e3829ce639214ba5f5a4c7c8efc699e1776801a6
7
+ data.tar.gz: 356194bce279f215d58ea36e0e7e188c4ee134a4a4b55ce435fdee255caf23c39974f0b87a305e332a84b7aaf519db6ee13525beecd47117b9f9ebbd765679e3
@@ -5,4 +5,11 @@ steps:
5
5
  - bundle exec rake db:drop
6
6
  - bundle exec rake db:create
7
7
  - RACK_ENV=test bundle exec rake db:migrate
8
- - bundle exec rspec
8
+ - bundle exec rspec
9
+
10
+ - block: ":rubygems: Publish :red_button:"
11
+ branches: "main"
12
+
13
+ - name: "Push :rubygems:"
14
+ commands: "./bin/publish"
15
+ branches: "main"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osso (0.0.3.11)
4
+ osso (0.0.3.16)
5
5
  activesupport (>= 6.0.3.2)
6
6
  graphql
7
7
  jwt
@@ -66,7 +66,7 @@ GEM
66
66
  method_source (1.0.0)
67
67
  mini_portile2 (2.4.0)
68
68
  minitest (5.14.1)
69
- multi_json (1.14.1)
69
+ multi_json (1.15.0)
70
70
  mustermann (1.1.1)
71
71
  ruby2_keywords (~> 0.0.1)
72
72
  nokogiri (1.10.9)
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+ # Scriptacular - gemify.sh
3
+ # Create a Ruby gem and push it to rubygems.org
4
+ # Copyright 2013 Christopher Simpkins
5
+ # MIT License
6
+
7
+ GEM_NAME="osso-rb"
8
+ GEMSPEC_SUFFIX=".gemspec"
9
+
10
+ # run the gem build and parse for the gem release filename
11
+ GEM_BUILD_NAME=$(gem build "$GEM_NAME$GEMSPEC_SUFFIX" | awk '/File/ {print $2}' -)
12
+
13
+ if [ -z "$GEM_BUILD_NAME" ]; then
14
+ echo "The gem build failed." >&2
15
+ exit 1
16
+ fi
17
+
18
+ gem push $GEM_BUILD_NAME
@@ -12,6 +12,8 @@ module Osso
12
12
  field :create_oauth_client, mutation: Mutations::CreateOauthClient
13
13
  field :delete_enterprise_account, mutation: Mutations::DeleteEnterpriseAccount
14
14
  field :delete_oauth_client, mutation: Mutations::DeleteOauthClient
15
+ field :set_redirect_uris, mutation: Mutations::SetRedirectUris
16
+ field :regenerate_oauth_credentials, mutation: Mutations::RegenerateOauthCredentials
15
17
  end
16
18
  end
17
19
  end
@@ -12,3 +12,5 @@ require_relative 'mutations/create_enterprise_account'
12
12
  require_relative 'mutations/create_oauth_client'
13
13
  require_relative 'mutations/delete_enterprise_account'
14
14
  require_relative 'mutations/delete_oauth_client'
15
+ require_relative 'mutations/regenerate_oauth_credentials'
16
+ require_relative 'mutations/set_redirect_uris'
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class RegenerateOauthCredentials < BaseMutation
7
+ null false
8
+
9
+ argument :id, ID, required: true
10
+
11
+ field :oauth_client, Types::OauthClient, null: false
12
+ field :errors, [String], null: false
13
+
14
+ def resolve(id:)
15
+ oauth_client = Osso::Models::OauthClient.find(id)
16
+ oauth_client.generate_secrets
17
+
18
+ return response_data(oauth_client: oauth_client) if oauth_client.save
19
+
20
+ response_error(errors: oauth_client.errors.full_messages)
21
+ end
22
+
23
+ def ready?(*)
24
+ return true if context[:scope] == :admin
25
+
26
+ raise ::GraphQL::ExecutionError, 'Only admin users may mutate OauthClients'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class SetRedirectUris < BaseMutation
7
+ null false
8
+
9
+ argument :id, ID, required: true
10
+ argument :redirect_uris, [Types::RedirectUrisInput], required: true
11
+
12
+ field :oauth_client, Types::OauthClient, null: true
13
+ field :errors, [String], null: false
14
+
15
+ def resolve(id:, redirect_uris:)
16
+ oauth_client = Osso::Models::OauthClient.find(id)
17
+
18
+ update_existing(oauth_client, redirect_uris)
19
+ create_new(oauth_client, redirect_uris)
20
+
21
+ response_data(oauth_client: oauth_client.reload)
22
+ rescue StandardError => e
23
+ response_error(errors: e)
24
+ end
25
+
26
+ def ready?(*)
27
+ return true if context[:scope] == :admin
28
+
29
+ raise ::GraphQL::ExecutionError, 'Only admin users may mutate OauthClients'
30
+ end
31
+
32
+ def update_existing(oauth_client, redirect_uris)
33
+ oauth_client.redirect_uris.each do |redirect|
34
+ updating_index = redirect_uris.index { |incoming| incoming[:id] == redirect.id }
35
+
36
+ if updating_index
37
+ updating = redirect_uris.delete_at(updating_index)
38
+ redirect.update(updating.to_h)
39
+ next
40
+ end
41
+
42
+ redirect.destroy
43
+ end
44
+ end
45
+
46
+ def create_new(oauth_client, redirect_uris)
47
+ redirect_uris.map do |uri|
48
+ oauth_client.redirect_uris.create(uri.to_h.without(:id))
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -8,12 +8,13 @@ module Osso
8
8
  argument :sort_column, String, required: false
9
9
  argument :sort_order, String, required: false
10
10
  end
11
- field :oauth_clients, null: true, resolver: Resolvers::OAuthClients
12
11
 
13
12
  field :enterprise_account, null: true, resolver: Resolvers::EnterpriseAccount do
14
13
  argument :domain, String, required: true
15
14
  end
16
15
 
16
+ field :oauth_clients, null: true, resolver: Resolvers::OAuthClients
17
+
17
18
  field(
18
19
  :identity_provider,
19
20
  Types::IdentityProvider,
@@ -22,6 +23,15 @@ module Osso
22
23
  ) do
23
24
  argument :id, ID, required: true
24
25
  end
26
+
27
+ field(
28
+ :oauth_client,
29
+ Types::OauthClient,
30
+ null: true,
31
+ resolve: ->(_obj, args, _context) { Osso::Models::OauthClient.find(args[:id]) },
32
+ ) do
33
+ argument :id, ID, required: true
34
+ end
25
35
  end
26
36
  end
27
37
  end
@@ -13,5 +13,7 @@ require_relative 'types/identity_provider_service'
13
13
  require_relative 'types/identity_provider_status'
14
14
  require_relative 'types/identity_provider'
15
15
  require_relative 'types/enterprise_account'
16
+ require_relative 'types/redirect_uri'
17
+ require_relative 'types/redirect_uri_input'
16
18
  require_relative 'types/oauth_client'
17
19
  require_relative 'types/user'
@@ -14,6 +14,7 @@ module Osso
14
14
  field :name, String, null: false
15
15
  field :client_id, String, null: false
16
16
  field :client_secret, String, null: false
17
+ field :redirect_uris, [Types::RedirectUri], null: true
17
18
 
18
19
  def client_id
19
20
  object.identifier
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class RedirectUri < Types::BaseObject
9
+ description 'An allowed redirect URI for an OauthClient'
10
+ implements ::GraphQL::Types::Relay::Node
11
+
12
+ global_id_field :gid
13
+ field :id, ID, null: false
14
+ field :uri, String, null: false
15
+ field :primary, Boolean, null: false
16
+
17
+ def self.authorized?(object, context)
18
+ super && context[:scope] == :admin
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class RedirectUrisInput < Types::BaseInputObject
9
+ description 'Attributes for creating or updating a collection of redirect URIs for an Oauth Client'
10
+ argument :id, ID, 'Database ID', required: false
11
+ argument :uri, String, 'URI value', required: true
12
+ argument :primary, Boolean, 'Whether the URI is the primary uri used in IDP initiated login', required: true
13
+ end
14
+ end
15
+ end
16
+ end
@@ -14,6 +14,8 @@ module Osso
14
14
  redirect ENV['JWT_URL']
15
15
  end
16
16
 
17
+ # use client id in payload to restrict customer
18
+ # users from accessing dev?
17
19
  def enterprise_authorized?(_domain)
18
20
  payload, _args = decode(token)
19
21
 
@@ -19,20 +19,14 @@ module Osso
19
19
  end
20
20
 
21
21
  def saml_options
22
- attributes.slice(
23
- 'domain',
24
- 'idp_cert',
25
- 'idp_sso_target_url',
26
- ).symbolize_keys
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'),
@@ -9,11 +9,11 @@ module Osso
9
9
  has_many :identity_providers
10
10
  has_many :redirect_uris
11
11
 
12
- before_validation :setup, on: :create
12
+ before_validation :generate_secrets, on: :create
13
13
  validates :name, :secret, presence: true
14
14
  validates :identifier, presence: true, uniqueness: true
15
15
 
16
- def default_redirect_uri
16
+ def primary_redirect_uri
17
17
  redirect_uris.find(&:primary)
18
18
  end
19
19
 
@@ -21,9 +21,7 @@ module Osso
21
21
  redirect_uris.map(&:uri)
22
22
  end
23
23
 
24
- private
25
-
26
- def setup
24
+ def generate_secrets
27
25
  self.identifier = SecureRandom.hex(16)
28
26
  self.secret = SecureRandom.hex(32)
29
27
  end
@@ -4,17 +4,6 @@ module Osso
4
4
  module Models
5
5
  class RedirectUri < ActiveRecord::Base
6
6
  belongs_to :oauth_client
7
-
8
- # TODO
9
- # before_validation :set_primary, on: :creaet, :update
10
-
11
- private
12
-
13
- def set_primary
14
- if primary_was.true? && primary.false?
15
-
16
- end
17
- end
18
7
  end
19
8
  end
20
9
  end
@@ -36,6 +36,12 @@ module Osso
36
36
 
37
37
  erb :admin
38
38
  end
39
+
40
+ get '/config/:id' do
41
+ admin_protected!
42
+
43
+ erb :admin
44
+ end
39
45
  end
40
46
  end
41
47
  end
@@ -14,20 +14,16 @@ module Osso
14
14
  /[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/.
15
15
  freeze
16
16
 
17
- def self.internal_redirect?(env)
18
- env['HTTP_REFERER']&.match(env['SERVER_NAME'])
19
- end
20
-
21
17
  use OmniAuth::Builder do
22
18
  OmniAuth::MultiProvider.register(
23
19
  self,
24
20
  provider_name: 'saml',
25
21
  identity_provider_id_regex: UUID_REGEXP,
26
- path_prefix: '/saml',
22
+ path_prefix: '/auth/saml',
27
23
  callback_suffix: 'callback',
28
24
  ) do |identity_provider_id, _env|
29
- provider = Models::IdentityProvider.find(identity_provider_id)
30
- provider.saml_options
25
+ Models::IdentityProvider.find(identity_provider_id).
26
+ saml_options
31
27
  end
32
28
  end
33
29
 
@@ -36,11 +32,10 @@ module Osso
36
32
  # their Identity Provider. We find or create a user record,
37
33
  # and then create an authorization code for that user. The user
38
34
  # is redirected back to your application with this code
39
- # as a URL query param, which you then exhange for an access token
35
+ # as a URL query param, which you then exchange for an access token.
40
36
  post '/saml/:id/callback' do
41
37
  provider = Models::IdentityProvider.find(params[:id])
42
- oauth_client = provider.oauth_client
43
- redirect_uri = env['redirect_uri'] || oauth_client.default_redirect_uri.uri
38
+ @oauth_client = provider.oauth_client
44
39
 
45
40
  attributes = env['omniauth.auth']&.
46
41
  extra&.
@@ -56,11 +51,29 @@ module Osso
56
51
  end
57
52
 
58
53
  authorization_code = user.authorization_codes.create!(
59
- oauth_client: oauth_client,
54
+ oauth_client: @oauth_client,
60
55
  redirect_uri: redirect_uri,
61
56
  )
62
57
 
63
- redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{session[:oauth_state]}")
58
+ # Mark IDP as active
59
+
60
+ redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{provider_state}")
61
+ end
62
+
63
+ def redirect_uri
64
+ return @oauth_client.primary_redirect_uri.uri if valid_idp_initiated_flow
65
+
66
+ session[:osso_oauth_redirect_uri]
67
+ end
68
+
69
+ def provider_state
70
+ return 'IDP_INITIATED' if valid_idp_initiated_flow
71
+
72
+ session[:osso_oauth_state]
73
+ end
74
+
75
+ def valid_idp_initiated_flow
76
+ !session[:osso_oauth_redirect_uri] && !session[:osso_oauth_state]
64
77
  end
65
78
  end
66
79
  end
@@ -6,7 +6,7 @@ module Osso
6
6
  class Oauth < Sinatra::Base
7
7
  include AppConfig
8
8
  register Sinatra::Namespace
9
- # rubocop:disable Metrics/BlockLength
9
+
10
10
  namespace '/oauth' do
11
11
  # Send your users here in order to being an authentication
12
12
  # flow. This flow follows the authorization grant oauth
@@ -19,11 +19,11 @@ module Osso
19
19
 
20
20
  Rack::OAuth2::Server::Authorize.new do |req, _res|
21
21
  client = Models::OauthClient.find_by!(identifier: req.client_id)
22
- req.verify_redirect_uri!(client.redirect_uri_values)
22
+ session[:osso_oauth_redirect_uri] = req.verify_redirect_uri!(client.redirect_uri_values)
23
23
  end.call(env)
24
24
 
25
25
  if @enterprise.single_provider?
26
- session[:oauth_state] = params[:state]
26
+ session[:osso_oauth_state] = params[:state]
27
27
  redirect "/auth/saml/#{@enterprise.provider.id}"
28
28
  end
29
29
 
@@ -35,9 +35,10 @@ module Osso
35
35
  return erb :error
36
36
  end
37
37
 
38
- # Exchange an authorization code token for an access token.
39
- # In addition to the token, you must include all paramaters
40
- # required by Oauth spec: redirect_uri, client ID, and client secret
38
+ # 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,
41
+ # and client secret
41
42
  post '/token' do
42
43
  Rack::OAuth2::Server::Token.new do |req, res|
43
44
  code = Models::AuthorizationCode.
@@ -60,4 +61,3 @@ module Osso
60
61
  end
61
62
  end
62
63
  end
63
- # rubocop:enable Metrics/BlockLength
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.3.11'
4
+ VERSION = '0.0.3.16'
5
5
  end
@@ -14,4 +14,16 @@ describe Osso::Models::IdentityProvider do
14
14
  )
15
15
  end
16
16
  end
17
+
18
+ describe '#saml_options' do
19
+ it 'returns the required args' do
20
+ expect(subject.saml_options).
21
+ to match(
22
+ domain: subject.domain,
23
+ idp_cert: subject.sso_cert,
24
+ idp_sso_target_url: subject.sso_url,
25
+ issuer: subject.domain,
26
+ )
27
+ end
28
+ end
17
29
  end
@@ -3,6 +3,31 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Osso::Auth do
6
+ describe 'get /auth/saml/:uuid' do
7
+ describe 'for an Okta SAML provider' do
8
+ let(:enterprise) { create(:enterprise_with_okta) }
9
+ let(:okta_provider) { enterprise.identity_providers.first }
10
+ it 'uses omniauth saml' do
11
+ get("/auth/saml/#{okta_provider.id}")
12
+
13
+ expect(last_response).to be_redirect
14
+ follow_redirect!
15
+ expect(last_request.url).to match("auth/saml/#{okta_provider.id}")
16
+ end
17
+ end
18
+
19
+ describe 'for an Azure SAML provider' do
20
+ let(:enterprise) { create(:enterprise_with_okta) }
21
+ let(:azure_provider) { enterprise.identity_providers.first }
22
+ it 'uses omniauth saml' do
23
+ get("/auth/saml/#{azure_provider.id}")
24
+
25
+ expect(last_response).to be_redirect
26
+ follow_redirect!
27
+ expect(last_request.url).to match("auth/saml/#{azure_provider.id}")
28
+ end
29
+ end
30
+ end
6
31
  describe 'post /auth/saml/:uuid/callback' do
7
32
  describe 'for an Okta SAML provider' do
8
33
  let(:enterprise) { create(:enterprise_with_okta) }
@@ -38,6 +63,24 @@ describe Osso::Auth do
38
63
  )
39
64
  end.to change { Osso::Models::AuthorizationCode.count }.by(1)
40
65
  end
66
+
67
+ describe 'for an IDP initiated login' do
68
+ it 'redirects with a default state' do
69
+ mock_saml_omniauth
70
+
71
+ post(
72
+ "/auth/saml/#{okta_provider.id}/callback",
73
+ nil,
74
+ {
75
+ 'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
76
+ 'identity_provider' => okta_provider,
77
+ },
78
+ )
79
+ expect(last_response).to be_redirect
80
+ follow_redirect!
81
+ expect(last_request.url).to match(/.*state=IDP_INITIATED$/)
82
+ end
83
+ end
41
84
  end
42
85
 
43
86
  describe 'on subsequent authentications' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3.11
4
+ version: 0.0.3.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Bauch
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-27 00:00:00.000000000 Z
11
+ date: 2020-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -226,6 +226,7 @@ email:
226
226
  executables:
227
227
  - annotate
228
228
  - console
229
+ - publish
229
230
  - setup
230
231
  extensions: []
231
232
  extra_rdoc_files: []
@@ -246,6 +247,7 @@ files:
246
247
  - Rakefile
247
248
  - bin/annotate
248
249
  - bin/console
250
+ - bin/publish
249
251
  - bin/setup
250
252
  - config/database.yml
251
253
  - db/schema.rb
@@ -278,6 +280,8 @@ files:
278
280
  - lib/osso/graphql/mutations/create_oauth_client.rb
279
281
  - lib/osso/graphql/mutations/delete_enterprise_account.rb
280
282
  - lib/osso/graphql/mutations/delete_oauth_client.rb
283
+ - lib/osso/graphql/mutations/regenerate_oauth_credentials.rb
284
+ - lib/osso/graphql/mutations/set_redirect_uris.rb
281
285
  - lib/osso/graphql/query.rb
282
286
  - lib/osso/graphql/resolvers.rb
283
287
  - lib/osso/graphql/resolvers/enterprise_account.rb
@@ -294,6 +298,8 @@ files:
294
298
  - lib/osso/graphql/types/identity_provider_service.rb
295
299
  - lib/osso/graphql/types/identity_provider_status.rb
296
300
  - lib/osso/graphql/types/oauth_client.rb
301
+ - lib/osso/graphql/types/redirect_uri.rb
302
+ - lib/osso/graphql/types/redirect_uri_input.rb
297
303
  - lib/osso/graphql/types/user.rb
298
304
  - lib/osso/helpers/auth.rb
299
305
  - lib/osso/helpers/helpers.rb
@@ -348,7 +354,7 @@ homepage: https://github.com/enterprise-oss/osso-rb
348
354
  licenses:
349
355
  - MIT
350
356
  metadata: {}
351
- post_install_message:
357
+ post_install_message:
352
358
  rdoc_options: []
353
359
  require_paths:
354
360
  - lib
@@ -364,7 +370,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
364
370
  version: '0'
365
371
  requirements: []
366
372
  rubygems_version: 3.0.3
367
- signing_key:
373
+ signing_key:
368
374
  specification_version: 4
369
375
  summary: Main functionality for Osso
370
376
  test_files: []