osso 0.0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.buildkite/hooks/environment +9 -0
- data/.buildkite/hooks/pre-command +7 -0
- data/.buildkite/pipeline.yml +6 -0
- data/.buildkite/template.yml +5 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.rubocop.yml +81 -0
- data/CODE_OF_CONDUCT.md +130 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +176 -0
- data/LICENSE +111 -0
- data/README.md +2 -0
- data/Rakefile +14 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/config/database.yml +14 -0
- data/db/schema.rb +133 -0
- data/lib/osso.rb +11 -0
- data/lib/osso/Rakefile +13 -0
- data/lib/osso/db/migrate/20190909230109_enable_uuid.rb +7 -0
- data/lib/osso/db/migrate/20200328135750_create_users.rb +12 -0
- data/lib/osso/db/migrate/20200328143303_create_oauth_tables.rb +57 -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 +7 -0
- data/lib/osso/db/migrate/20200411192645_create_enterprise_accounts.rb +15 -0
- data/lib/osso/db/migrate/20200413132407_add_oauth_clients.rb +13 -0
- data/lib/osso/db/migrate/20200413142511_create_authorization_codes.rb +15 -0
- data/lib/osso/db/migrate/20200413163451_create_access_tokens.rb +13 -0
- data/lib/osso/db/migrate/20200502120616_create_redirect_uris_and_drop_from_oauth_clients.rb +13 -0
- 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 +16 -0
- data/lib/osso/graphql/mutations.rb +12 -0
- data/lib/osso/graphql/mutations/base_mutation.rb +41 -0
- data/lib/osso/graphql/mutations/configure_identity_provider.rb +36 -0
- data/lib/osso/graphql/mutations/create_enterprise_account.rb +25 -0
- data/lib/osso/graphql/mutations/create_identity_provider.rb +30 -0
- data/lib/osso/graphql/mutations/set_identity_provider.rb +27 -0
- data/lib/osso/graphql/query.rb +25 -0
- data/lib/osso/graphql/resolvers.rb +12 -0
- data/lib/osso/graphql/resolvers/enterprise_account.rb +25 -0
- data/lib/osso/graphql/resolvers/enterprise_accounts.rb +17 -0
- data/lib/osso/graphql/resolvers/oauth_clients.rb +15 -0
- data/lib/osso/graphql/schema.rb +46 -0
- data/lib/osso/graphql/types.rb +15 -0
- data/lib/osso/graphql/types/base_enum.rb +10 -0
- data/lib/osso/graphql/types/base_input_object.rb +10 -0
- data/lib/osso/graphql/types/base_object.rb +12 -0
- data/lib/osso/graphql/types/enterprise_account.rb +33 -0
- data/lib/osso/graphql/types/identity_provider.rb +37 -0
- data/lib/osso/graphql/types/identity_provider_service.rb +12 -0
- data/lib/osso/graphql/types/oauth_client.rb +20 -0
- data/lib/osso/graphql/types/user.rb +17 -0
- data/lib/osso/helpers/auth.rb +71 -0
- data/lib/osso/helpers/helpers.rb +8 -0
- data/lib/osso/lib/app_config.rb +20 -0
- data/lib/osso/lib/oauth2_token.rb +38 -0
- data/lib/osso/lib/route_map.rb +28 -0
- data/lib/osso/models/access_token.rb +29 -0
- data/lib/osso/models/authorization_code.rb +14 -0
- data/lib/osso/models/enterprise_account.rb +28 -0
- data/lib/osso/models/identity_provider.rb +48 -0
- data/lib/osso/models/models.rb +16 -0
- data/lib/osso/models/oauth_client.rb +32 -0
- data/lib/osso/models/redirect_uri.rb +20 -0
- data/lib/osso/models/saml_provider.rb +49 -0
- data/lib/osso/models/saml_providers/azure_saml_provider.rb +22 -0
- data/lib/osso/models/saml_providers/okta_saml_provider.rb +23 -0
- data/lib/osso/models/user.rb +24 -0
- data/lib/osso/rake.rb +4 -0
- data/lib/osso/routes/admin.rb +41 -0
- data/lib/osso/routes/auth.rb +67 -0
- data/lib/osso/routes/oauth.rb +63 -0
- data/lib/osso/routes/routes.rb +10 -0
- data/lib/osso/routes/views/error.erb +1 -0
- data/lib/osso/routes/views/multiple_providers.erb +1 -0
- data/lib/osso/version.rb +5 -0
- data/lib/tasks/bootstrap.rake +16 -0
- data/osso-rb.gemspec +40 -0
- data/spec/factories/authorization_code.rb +10 -0
- data/spec/factories/enterprise_account.rb +46 -0
- data/spec/factories/identity_providers.rb +49 -0
- data/spec/factories/oauth_client.rb +12 -0
- data/spec/factories/redirect_uri.rb +14 -0
- data/spec/factories/user.rb +18 -0
- 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 +19 -0
- data/spec/models/identity_provider_spec.rb +17 -0
- data/spec/models/okta_saml_provider_spec.rb +20 -0
- data/spec/routes/admin_spec.rb +60 -0
- data/spec/routes/app_spec.rb +6 -0
- data/spec/routes/auth_spec.rb +112 -0
- data/spec/routes/oauth_spec.rb +134 -0
- data/spec/spec_helper.rb +68 -0
- data/spec/support/spec_app.rb +9 -0
- data/spec/support/views/admin.erb +5 -0
- metadata +348 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sinatra/activerecord'
|
4
|
+
|
5
|
+
module Osso
|
6
|
+
module Models
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'access_token'
|
11
|
+
require_relative 'authorization_code'
|
12
|
+
require_relative 'enterprise_account'
|
13
|
+
require_relative 'oauth_client'
|
14
|
+
require_relative 'redirect_uri'
|
15
|
+
require_relative 'identity_provider'
|
16
|
+
require_relative 'user'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
module Osso
|
5
|
+
module Models
|
6
|
+
class OauthClient < ActiveRecord::Base
|
7
|
+
has_many :access_tokens
|
8
|
+
has_many :refresh_tokens
|
9
|
+
has_many :identity_providers
|
10
|
+
has_many :redirect_uris
|
11
|
+
|
12
|
+
before_validation :setup, on: :create
|
13
|
+
validates :name, :secret, presence: true
|
14
|
+
validates :identifier, presence: true, uniqueness: true
|
15
|
+
|
16
|
+
def default_redirect_uri
|
17
|
+
redirect_uris.find(&:primary)
|
18
|
+
end
|
19
|
+
|
20
|
+
def redirect_uri_values
|
21
|
+
redirect_uris.map(&:uri)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def setup
|
27
|
+
self.identifier = SecureRandom.hex(16)
|
28
|
+
self.secret = SecureRandom.hex(64)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Osso
|
4
|
+
module Models
|
5
|
+
class RedirectUri < ActiveRecord::Base
|
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
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
+
before_create :create_enterprise_account
|
13
|
+
|
14
|
+
# def name
|
15
|
+
# raise(
|
16
|
+
# NoMethodError,
|
17
|
+
# '#name must be defined on each provider specific subclass',
|
18
|
+
# )
|
19
|
+
# end
|
20
|
+
|
21
|
+
# def saml_options
|
22
|
+
# raise(
|
23
|
+
# NoMethodError,
|
24
|
+
# '#saml_options must be defined on each provider specific subclass',
|
25
|
+
# )
|
26
|
+
# end
|
27
|
+
|
28
|
+
def assertion_consumer_service_url
|
29
|
+
[
|
30
|
+
ENV.fetch('BASE_URL'),
|
31
|
+
'auth',
|
32
|
+
'saml',
|
33
|
+
id,
|
34
|
+
'callback',
|
35
|
+
].join('/')
|
36
|
+
end
|
37
|
+
|
38
|
+
alias acs_url assertion_consumer_service_url
|
39
|
+
|
40
|
+
def create_enterprise_account
|
41
|
+
return if enterprise_account_id
|
42
|
+
|
43
|
+
self.enterprise_account = Models::EnterpriseAccount.create(
|
44
|
+
domain: domain,
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -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::IdentityProvider
|
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::IdentityProvider
|
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 :identity_provider
|
8
|
+
has_many :authorization_codes, dependent: :delete_all
|
9
|
+
has_many :access_tokens, dependent: :delete_all
|
10
|
+
|
11
|
+
def oauth_client
|
12
|
+
identity_provider.oauth_client
|
13
|
+
end
|
14
|
+
|
15
|
+
def as_json(*)
|
16
|
+
{
|
17
|
+
email: email,
|
18
|
+
id: id,
|
19
|
+
idp: identity_provider.name,
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/osso/rake.rb
ADDED
@@ -0,0 +1,41 @@
|
|
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
|
+
register Sinatra::Namespace
|
10
|
+
|
11
|
+
before do
|
12
|
+
chomp_token
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace '/admin' do
|
16
|
+
get '' do
|
17
|
+
admin_protected!
|
18
|
+
|
19
|
+
erb :admin
|
20
|
+
end
|
21
|
+
|
22
|
+
get '/enterprise' do
|
23
|
+
admin_protected!
|
24
|
+
|
25
|
+
erb :admin
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/enterprise/:domain' do
|
29
|
+
enterprise_protected!(params[:domain])
|
30
|
+
|
31
|
+
erb :admin
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/config' do
|
35
|
+
admin_protected!
|
36
|
+
|
37
|
+
erb :admin
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
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
|
+
register Sinatra::Namespace
|
12
|
+
|
13
|
+
UUID_REGEXP =
|
14
|
+
/[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/.
|
15
|
+
freeze
|
16
|
+
|
17
|
+
def self.internal_redirect?(env)
|
18
|
+
env['HTTP_REFERER']&.match(env['SERVER_NAME'])
|
19
|
+
end
|
20
|
+
|
21
|
+
use OmniAuth::Builder do
|
22
|
+
OmniAuth::MultiProvider.register(
|
23
|
+
self,
|
24
|
+
provider_name: 'saml',
|
25
|
+
identity_provider_id_regex: UUID_REGEXP,
|
26
|
+
path_prefix: '/saml',
|
27
|
+
callback_suffix: 'callback',
|
28
|
+
) do |identity_provider_id, _env|
|
29
|
+
provider = Models::IdentityProvider.find(identity_provider_id)
|
30
|
+
provider.saml_options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
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
|
44
|
+
|
45
|
+
attributes = env['omniauth.auth']&.
|
46
|
+
extra&.
|
47
|
+
response_object&.
|
48
|
+
attributes
|
49
|
+
|
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
|
57
|
+
|
58
|
+
authorization_code = user.authorization_codes.create!(
|
59
|
+
oauth_client: oauth_client,
|
60
|
+
redirect_uri: redirect_uri,
|
61
|
+
)
|
62
|
+
|
63
|
+
redirect(redirect_uri + "?code=#{CGI.escape(authorization_code.token)}&state=#{session[:oauth_state]}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/oauth2'
|
4
|
+
|
5
|
+
module Osso
|
6
|
+
class Oauth < Sinatra::Base
|
7
|
+
include AppConfig
|
8
|
+
register Sinatra::Namespace
|
9
|
+
# rubocop:disable Metrics/BlockLength
|
10
|
+
namespace '/oauth' do
|
11
|
+
# Send your users here in order to being an authentication
|
12
|
+
# flow. This flow follows the authorization grant oauth
|
13
|
+
# spec with one exception - you must also pass the domain
|
14
|
+
# of the user who wants to sign in.
|
15
|
+
get '/authorize' do
|
16
|
+
@enterprise = Models::EnterpriseAccount.
|
17
|
+
includes(:identity_providers).
|
18
|
+
find_by!(domain: params[:domain])
|
19
|
+
|
20
|
+
Rack::OAuth2::Server::Authorize.new do |req, _res|
|
21
|
+
client = Models::OauthClient.find_by!(identifier: req.client_id)
|
22
|
+
req.verify_redirect_uri!(client.redirect_uri_values)
|
23
|
+
end.call(env)
|
24
|
+
|
25
|
+
if @enterprise.single_provider?
|
26
|
+
session[:oauth_state] = params[:state]
|
27
|
+
redirect "/auth/saml/#{@enterprise.provider.id}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO: multiple provider support
|
31
|
+
# erb :multiple_providers
|
32
|
+
|
33
|
+
rescue Rack::OAuth2::Server::Authorize::BadRequest => e
|
34
|
+
@error = e
|
35
|
+
return erb :error
|
36
|
+
end
|
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
|
41
|
+
post '/token' do
|
42
|
+
Rack::OAuth2::Server::Token.new do |req, res|
|
43
|
+
code = Models::AuthorizationCode.
|
44
|
+
find_by_token!(params[:code])
|
45
|
+
client = Models::OauthClient.find_by!(identifier: req.client_id)
|
46
|
+
req.invalid_client! if client.secret != req.client_secret
|
47
|
+
req.invalid_grant! if code.redirect_uri != req.redirect_uri
|
48
|
+
res.access_token = code.access_token.to_bearer_token
|
49
|
+
end.call(env)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Use the access token to request a user profile
|
53
|
+
get '/me' do
|
54
|
+
json Models::AccessToken.
|
55
|
+
includes(:user).
|
56
|
+
valid.
|
57
|
+
find_by_token!(params[:access_token]).
|
58
|
+
user
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @error %>
|
@@ -0,0 +1 @@
|
|
1
|
+
multiple providers
|
data/lib/osso/version.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sinatra/activerecord'
|
4
|
+
require 'sinatra/activerecord/rake'
|
5
|
+
require 'osso'
|
6
|
+
|
7
|
+
namespace :osso do
|
8
|
+
desc 'Bootstrap Osso data for a deployment'
|
9
|
+
task :bootstrap do
|
10
|
+
%w[Production Staging Development].each do |environement|
|
11
|
+
Osso::Models::OauthClient.create!(
|
12
|
+
name: environement,
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/osso-rb.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/osso/version'
|
4
|
+
|
5
|
+
# rubocop:disable Metrics/BlockLength
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'osso'
|
8
|
+
spec.version = Osso::VERSION
|
9
|
+
spec.authors = ['Sam Bauch']
|
10
|
+
spec.email = ['sbauch@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = 'Main functionality for Osso'
|
13
|
+
spec.description = 'This gem includes the main functionality for Osso apps,'
|
14
|
+
spec.homepage = 'https://github.com/enterprise-oss/osso-rb'
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.add_runtime_dependency 'activesupport', '>= 6.0.3.2'
|
19
|
+
spec.add_runtime_dependency 'graphql'
|
20
|
+
spec.add_runtime_dependency 'jwt'
|
21
|
+
spec.add_runtime_dependency 'omniauth-multi-provider'
|
22
|
+
spec.add_runtime_dependency 'omniauth-saml'
|
23
|
+
spec.add_runtime_dependency 'rack', '>= 2.1.4'
|
24
|
+
spec.add_runtime_dependency 'rack-contrib'
|
25
|
+
spec.add_runtime_dependency 'rack-oauth2'
|
26
|
+
spec.add_runtime_dependency 'rake'
|
27
|
+
spec.add_runtime_dependency 'sinatra'
|
28
|
+
spec.add_runtime_dependency 'sinatra-activerecord'
|
29
|
+
spec.add_runtime_dependency 'sinatra-contrib'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 2.1'
|
32
|
+
spec.add_development_dependency 'pry'
|
33
|
+
|
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.require_paths = ['lib']
|
39
|
+
end
|
40
|
+
# rubocop:enable Metrics/BlockLength
|