diff --git a/Gemfile b/Gemfile index 56d286ac..099de8f3 100644 --- a/Gemfile +++ b/Gemfile @@ -10,10 +10,10 @@ gemspec # Git. Remember to move these dependencies to your gemspec before releasing # your gem to rubygems.org. -gem "rails", ">= 5.2.4.6", "< 6.2" +gem 'rails', '>= 5.2.4.6', '< 6.2' group :development, :test do - gem 'rubocop' gem 'pry' gem 'pry-nav' + gem 'rubocop' end diff --git a/Rakefile b/Rakefile index 435cbb33..a243ca4d 100644 --- a/Rakefile +++ b/Rakefile @@ -14,21 +14,19 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__) +APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__) load 'rails/tasks/engine.rake' - load 'rails/tasks/statistics.rake' - Bundler::GemHelper.install_tasks -Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f } +Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f } require 'rspec/core' require 'rspec/core/rake_task' -desc "Run all specs in spec directory (excluding plugin specs)" -RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare') +desc 'Run all specs in spec directory (excluding plugin specs)' +RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare') -task :default => :spec +task default: :spec diff --git a/app/controllers/concerns/scim_rails/response.rb b/app/controllers/concerns/scim_rails/response.rb index 00bbbe67..7dea99f8 100644 --- a/app/controllers/concerns/scim_rails/response.rb +++ b/app/controllers/concerns/scim_rails/response.rb @@ -2,7 +2,7 @@ module ScimRails module Response - CONTENT_TYPE = "application/scim+json" + CONTENT_TYPE = 'application/scim+json' def json_response(object, status = :ok) render \ @@ -13,12 +13,12 @@ def json_response(object, status = :ok) def json_scim_response(object:, status: :ok, counts: nil) case params[:action] - when "index" + when 'index' render \ json: list_response(object, counts), status: status, content_type: CONTENT_TYPE - when "show", "create", "put_update", "patch_update" + when 'show', 'create', 'put_update', 'patch_update' render \ json: object_response(object), status: status, @@ -28,67 +28,67 @@ def json_scim_response(object:, status: :ok, counts: nil) private - def list_response(object, counts) - object = object - .order(:id) - .offset(counts.offset) - .limit(counts.limit) - { - schemas: [ - "urn:ietf:params:scim:api:messages:2.0:ListResponse" - ], - totalResults: counts.total, - startIndex: counts.start_index, - itemsPerPage: counts.limit, - Resources: list_objects(object) - } - end + def list_response(object, counts) + object = object + .order(:id) + .offset(counts.offset) + .limit(counts.limit) + { + schemas: [ + 'urn:ietf:params:scim:api:messages:2.0:ListResponse' + ], + totalResults: counts.total, + startIndex: counts.start_index, + itemsPerPage: counts.limit, + Resources: list_objects(object), + } + end - def list_objects(objects) - objects.map do |object| - object_response(object) + def list_objects(objects) + objects.map do |object| + object_response(object) + end end - end - def object_response(object) - schema = case object - when ScimRails.config.scim_users_model - ScimRails.config.user_schema - when ScimRails.config.scim_groups_model - ScimRails.config.group_schema - else - raise ScimRails::ExceptionHandler::InvalidQuery, - "Unknown model: #{object}" - end - find_value(object, schema) - end + def object_response(object) + schema = case object + when ScimRails.config.scim_users_model + ScimRails.config.user_schema + when ScimRails.config.scim_groups_model + ScimRails.config.group_schema + else + raise ScimRails::ExceptionHandler::InvalidQuery, + "Unknown model: #{object}" + end + find_value(object, schema) + end - # `find_value` is a recursive method that takes a "user" and a - # "user schema" and replaces any symbols in the schema with the - # corresponding value from the user. Given a schema with symbols, - # `find_value` will search through the object for the symbols, - # send those symbols to the model, and replace the symbol with - # the return value. + # `find_value` is a recursive method that takes a "user" and a + # "user schema" and replaces any symbols in the schema with the + # corresponding value from the user. Given a schema with symbols, + # `find_value` will search through the object for the symbols, + # send those symbols to the model, and replace the symbol with + # the return value. - def find_value(object, schema) - case schema - when Hash - schema.each.with_object({}) do |(key, value), hash| - hash[key] = find_value(object, value) + def find_value(object, schema) + case schema + when Hash + schema.each.with_object({}) do |(key, value), hash| + hash[key] = find_value(object, value) + end + when Array, ActiveRecord::Associations::CollectionProxy + schema.map do |value| + find_value(object, value) + end + when ScimRails.config.scim_users_model + find_value(schema, ScimRails.config.user_abbreviated_schema) + when ScimRails.config.scim_groups_model + find_value(schema, ScimRails.config.group_abbreviated_schema) + when Symbol + find_value(object, object.public_send(schema)) + else + schema end - when Array, ActiveRecord::Associations::CollectionProxy - schema.map do |value| - find_value(object, value) - end - when ScimRails.config.scim_users_model - find_value(schema, ScimRails.config.user_abbreviated_schema) - when ScimRails.config.scim_groups_model - find_value(schema, ScimRails.config.group_abbreviated_schema) - when Symbol - find_value(object, object.public_send(schema)) - else - schema end - end end end diff --git a/app/controllers/scim_rails/application_controller.rb b/app/controllers/scim_rails/application_controller.rb index f9340d5c..7cde4a8f 100644 --- a/app/controllers/scim_rails/application_controller.rb +++ b/app/controllers/scim_rails/application_controller.rb @@ -10,63 +10,63 @@ class ApplicationController < ActionController::API private - def authorize_request - send(authentication_strategy) do |searchable_attribute, authentication_attribute| - authorization = AuthorizeApiRequest.new( - searchable_attribute: searchable_attribute, - authentication_attribute: authentication_attribute - ) - @company = authorization.company + def authorize_request + send(authentication_strategy) do |searchable_attribute, authentication_attribute| + authorization = AuthorizeApiRequest.new( + searchable_attribute: searchable_attribute, + authentication_attribute: authentication_attribute + ) + @company = authorization.company + end + raise ScimRails::ExceptionHandler::InvalidCredentials if @company.blank? end - raise ScimRails::ExceptionHandler::InvalidCredentials if @company.blank? - end - def authentication_strategy - if request.headers["Authorization"]&.include?("Bearer") - :authenticate_with_oauth_bearer - else - :authenticate_with_http_basic + def authentication_strategy + if request.headers['Authorization']&.include?('Bearer') + :authenticate_with_oauth_bearer + else + :authenticate_with_http_basic + end end - end - def authenticate_with_oauth_bearer - authentication_attribute = request.headers["Authorization"].split.last - payload = ScimRails::Encoder.decode(authentication_attribute).with_indifferent_access - searchable_attribute = payload[ScimRails.config.basic_auth_model_searchable_attribute] + def authenticate_with_oauth_bearer + authentication_attribute = request.headers['Authorization'].split.last + payload = ScimRails::Encoder.decode(authentication_attribute).with_indifferent_access + searchable_attribute = payload[ScimRails.config.basic_auth_model_searchable_attribute] - yield searchable_attribute, authentication_attribute - end + yield searchable_attribute, authentication_attribute + end - def find_value_for(attribute) - params.dig(*path_for(attribute)) - end + def find_value_for(attribute) + params.dig(*path_for(attribute)) + end - # `path_for` is a recursive method used to find the "path" for - # `.dig` to take when looking for a given attribute in the - # params. - # - # Example: `path_for(:name)` should return an array that looks - # like [:names, 0, :givenName]. `.dig` can then use that path - # against the params to translate the :name attribute to "John". + # `path_for` is a recursive method used to find the "path" for + # `.dig` to take when looking for a given attribute in the + # params. + # + # Example: `path_for(:name)` should return an array that looks + # like [:names, 0, :givenName]. `.dig` can then use that path + # against the params to translate the :name attribute to "John". - def path_for(attribute, object = controller_schema, path = []) - at_path = path.empty? ? object : object.dig(*path) - return path if at_path == attribute + def path_for(attribute, object = controller_schema, path = []) + at_path = path.empty? ? object : object.dig(*path) + return path if at_path == attribute - case at_path - when Hash - at_path.each do |key, _value| - found_path = path_for(attribute, object, [*path, key]) - return found_path if found_path - end - nil - when Array - at_path.each_with_index do |_value, index| - found_path = path_for(attribute, object, [*path, index]) - return found_path if found_path + case at_path + when Hash + at_path.each do |key, _value| + found_path = path_for(attribute, object, [*path, key]) + return found_path if found_path + end + nil + when Array + at_path.each_with_index do |_value, index| + found_path = path_for(attribute, object, [*path, index]) + return found_path if found_path + end + nil end - nil end - end end end diff --git a/app/controllers/scim_rails/scim_groups_controller.rb b/app/controllers/scim_rails/scim_groups_controller.rb index b9359f31..ce71c8f5 100644 --- a/app/controllers/scim_rails/scim_groups_controller.rb +++ b/app/controllers/scim_rails/scim_groups_controller.rb @@ -82,7 +82,7 @@ def destroy raise ScimRails::ExceptionHandler::InvalidConfiguration, e.message rescue ActiveRecord::RecordNotDestroyed => e raise ScimRails::ExceptionHandler::InvalidRequest, e.message - rescue => e + rescue StandardError => e raise ScimRails::ExceptionHandler::UnexpectedError, e.message end diff --git a/app/controllers/scim_rails/scim_users_controller.rb b/app/controllers/scim_rails/scim_users_controller.rb index 86c0b50e..6cac985c 100644 --- a/app/controllers/scim_rails/scim_users_controller.rb +++ b/app/controllers/scim_rails/scim_users_controller.rb @@ -82,7 +82,7 @@ def destroy raise ScimRails::ExceptionHandler::InvalidConfiguration, e.message rescue ActiveRecord::RecordNotDestroyed => e raise ScimRails::ExceptionHandler::InvalidRequest, e.message - rescue => e + rescue StandardError => e raise ScimRails::ExceptionHandler::UnexpectedError, e.message end diff --git a/app/models/scim_rails/authorize_api_request.rb b/app/models/scim_rails/authorize_api_request.rb index d9cde04e..68521f58 100644 --- a/app/models/scim_rails/authorize_api_request.rb +++ b/app/models/scim_rails/authorize_api_request.rb @@ -5,7 +5,9 @@ def initialize(searchable_attribute:, authentication_attribute:) @searchable_attribute = searchable_attribute @authentication_attribute = authentication_attribute - raise ScimRails::ExceptionHandler::InvalidCredentials if searchable_attribute.blank? || authentication_attribute.blank? + if searchable_attribute.blank? || authentication_attribute.blank? + raise ScimRails::ExceptionHandler::InvalidCredentials + end @search_parameter = { ScimRails.config.basic_auth_model_searchable_attribute => @searchable_attribute } end @@ -18,23 +20,20 @@ def company private - attr_reader :authentication_attribute - attr_reader :search_parameter - attr_reader :searchable_attribute - - def find_company - @company ||= ScimRails.config.basic_auth_model.find_by!(search_parameter) - - rescue ActiveRecord::RecordNotFound - raise ScimRails::ExceptionHandler::InvalidCredentials - end - - def authorize(authentication_model) - authorized = ActiveSupport::SecurityUtils.secure_compare( - authentication_model.public_send(ScimRails.config.basic_auth_model_authenticatable_attribute), - authentication_attribute - ) - raise ScimRails::ExceptionHandler::InvalidCredentials unless authorized - end + attr_reader :authentication_attribute, :search_parameter, :searchable_attribute + + def find_company + @company ||= ScimRails.config.basic_auth_model.find_by!(search_parameter) + rescue ActiveRecord::RecordNotFound + raise ScimRails::ExceptionHandler::InvalidCredentials + end + + def authorize(authentication_model) + authorized = ActiveSupport::SecurityUtils.secure_compare( + authentication_model.public_send(ScimRails.config.basic_auth_model_authenticatable_attribute), + authentication_attribute + ) + raise ScimRails::ExceptionHandler::InvalidCredentials unless authorized + end end end diff --git a/app/models/scim_rails/scim_count.rb b/app/models/scim_rails/scim_count.rb index f8c91754..73143a2c 100644 --- a/app/models/scim_rails/scim_count.rb +++ b/app/models/scim_rails/scim_count.rb @@ -10,17 +10,21 @@ class ScimCount def limit return 100 if @limit.blank? + validate_numericality(@limit) input = @limit.to_i raise if input < 1 + input end def start_index return 1 if @start_index.blank? + validate_numericality(@start_index) input = @start_index.to_i return 1 if input < 1 + input end @@ -30,9 +34,9 @@ def offset private - def validate_numericality(input) - raise unless input.match?(/\A\d+\z/) - end + def validate_numericality(input) + raise unless input.match?(/\A\d+\z/) + end end end diff --git a/app/models/scim_rails/scim_query_parser.rb b/app/models/scim_rails/scim_query_parser.rb index 13059610..df593462 100644 --- a/app/models/scim_rails/scim_query_parser.rb +++ b/app/models/scim_rails/scim_query_parser.rb @@ -5,7 +5,7 @@ class ScimQueryParser attr_accessor :query_elements, :query_attributes def initialize(query_string, queryable_attributes) - self.query_elements = query_string.gsub(/\[(.+?)\]/, ".0").split + self.query_elements = query_string.gsub(/\[(.+?)\]/, '.0').split self.query_attributes = queryable_attributes end @@ -13,8 +13,8 @@ def attribute attribute = query_elements[0] raise ScimRails::ExceptionHandler::InvalidQuery if attribute.blank? - dig_keys = attribute.split(".").map do |step| - step == "0" ? 0 : step.to_sym + dig_keys = attribute.split('.').map do |step| + step == '0' ? 0 : step.to_sym end mapped_attribute = query_attributes.dig(*dig_keys) @@ -28,22 +28,22 @@ def operator end def parameter - parameter = query_elements[2..-1].join(" ") + parameter = query_elements[2..-1].join(' ') return if parameter.blank? - parameter.gsub(/"/, "") + parameter.gsub(/"/, '') end private - def sql_comparison_operator(element) - case element - when "eq" - "=" - else - # TODO: implement additional query filters - raise ScimRails::ExceptionHandler::InvalidQuery + def sql_comparison_operator(element) + case element + when 'eq' + '=' + else + # TODO: implement additional query filters + raise ScimRails::ExceptionHandler::InvalidQuery + end end - end end end diff --git a/lib/generators/scim_rails/templates/initializer.rb b/lib/generators/scim_rails/templates/initializer.rb index 0e74b2fb..73f1738a 100644 --- a/lib/generators/scim_rails/templates/initializer.rb +++ b/lib/generators/scim_rails/templates/initializer.rb @@ -2,7 +2,7 @@ ScimRails.configure do |config| # Model used for authenticating and scoping users. - config.basic_auth_model = "Company" + config.basic_auth_model = 'Company' # Attribute used to search for a given record. This # attribute should be unique as it will return the @@ -14,7 +14,7 @@ config.basic_auth_model_authenticatable_attribute = :api_token # Model used for user records. - config.scim_users_model = "User" + config.scim_users_model = 'User' # Method used for retrieving user records from the # authenticatable model. @@ -25,7 +25,7 @@ config.scim_user_prevent_update_on_create = false # Model used for group records. - config.scim_groups_model = "Group" + config.scim_groups_model = 'Group' # Method used for retrieving user records from the # authenticatable model. config.scim_groups_scope = :groups @@ -56,16 +56,16 @@ userName: :email, givenName: :first_name, familyName: :last_name, - email: :email + email: :email, } # Array of attributes that can be modified on the # user model. If the attribute is not in this array # the attribute cannot be modified by this Gem. - config.mutable_user_attributes = [ - :first_name, - :last_name, - :email + config.mutable_user_attributes = %i[ + first_name + last_name + email ] # Hash of mutable attributes. This object is the map @@ -76,13 +76,13 @@ config.mutable_user_attributes_schema = { name: { givenName: :first_name, - familyName: :last_name + familyName: :last_name, }, emails: [ { - value: :email + value: :email, } - ] + ], } # Hash of SCIM structure for a user schema. This object @@ -93,31 +93,31 @@ # through as is, symbols will be passed to the user # object to return a value. config.user_schema = { - schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], + schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'], id: :id, userName: :email, name: { givenName: :first_name, - familyName: :last_name + familyName: :last_name, }, emails: [ { - value: :email + value: :email, } ], - active: :active? + active: :active?, } # Schema for users used in "abbreviated" lists such as in # the `members` field of a Group. config.user_abbreviated_schema = { value: :id, - display: :email + display: :email, } # Allow filtering Groups based on these parameters config.queryable_group_attributes = { - displayName: :name + displayName: :name, } # List of attributes on a Group that can be updated through SCIM @@ -131,7 +131,7 @@ # include all attributes listed in # config.mutable_group_attributes. config.mutable_group_attributes_schema = { - displayName: :name + displayName: :name, } # The User relation's IDs field name on the Group model. @@ -143,15 +143,15 @@ config.group_member_relation_schema = { value: :user_ids } config.group_schema = { - schemas: ["urn:ietf:params:scim:schemas:core:2.0:Group"], + schemas: ['urn:ietf:params:scim:schemas:core:2.0:Group'], id: :id, displayName: :name, - members: :users + members: :users, } config.group_abbreviated_schema = { value: :id, - display: :name + display: :name, } # Set group_destroy_method to a method on the Group model diff --git a/lib/scim_rails.rb b/lib/scim_rails.rb index e12b30b3..a8f0835b 100644 --- a/lib/scim_rails.rb +++ b/lib/scim_rails.rb @@ -1,6 +1,6 @@ -require "scim_rails/engine" -require "scim_rails/config" -require "scim_rails/encoder" +require 'scim_rails/engine' +require 'scim_rails/config' +require 'scim_rails/encoder' module ScimRails end diff --git a/lib/scim_rails/encoder.rb b/lib/scim_rails/encoder.rb index b5a264fd..4b4a8ef6 100644 --- a/lib/scim_rails/encoder.rb +++ b/lib/scim_rails/encoder.rb @@ -1,4 +1,4 @@ -require "jwt" +require 'jwt' module ScimRails module Encoder @@ -8,16 +8,18 @@ def encode(company) payload = { iat: Time.current.to_i, ScimRails.config.basic_auth_model_searchable_attribute => - company.public_send(ScimRails.config.basic_auth_model_searchable_attribute) + company.public_send(ScimRails.config.basic_auth_model_searchable_attribute), } - JWT.encode(payload, ScimRails.config.signing_secret, ScimRails.config.signing_algorithm) + JWT.encode(payload, ScimRails.config.signing_secret, + ScimRails.config.signing_algorithm) end def decode(token) verify = ScimRails.config.signing_algorithm != ScimRails::Config::ALGO_NONE - JWT.decode(token, ScimRails.config.signing_secret, verify, algorithm: ScimRails.config.signing_algorithm).first + JWT.decode(token, ScimRails.config.signing_secret, verify, + algorithm: ScimRails.config.signing_algorithm).first rescue JWT::VerificationError, JWT::DecodeError raise ScimRails::ExceptionHandler::InvalidCredentials end diff --git a/lib/scim_rails/engine.rb b/lib/scim_rails/engine.rb index 6ed3f6ae..44ef8325 100644 --- a/lib/scim_rails/engine.rb +++ b/lib/scim_rails/engine.rb @@ -3,8 +3,8 @@ class Engine < ::Rails::Engine isolate_namespace ScimRails config.generators do |g| - g.test_framework :rspec, :fixture => false - g.fixture_replacement :factory_bot, :dir => "spec/factories" + g.test_framework :rspec, fixture: false + g.fixture_replacement :factory_bot, dir: 'spec/factories' g.assets false g.helper false end diff --git a/scimaenaga.gemspec b/scimaenaga.gemspec index 9279c89a..2664a99d 100644 --- a/scimaenaga.gemspec +++ b/scimaenaga.gemspec @@ -1,29 +1,32 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('lib', __dir__) # Maintain your gem's version: -require "scim_rails/version" +require 'scim_rails/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "scimaenaga" + s.name = 'scimaenaga' s.version = ScimRails::VERSION s.authors = ['Studist Corporation'] - s.homepage = "https://github.com/StudistCorporation/scimaenaga" - s.summary = "SCIM Adapter for Rails." - s.description = "SCIM Adapter for Rails." - s.license = "MIT" + s.homepage = 'https://github.com/StudistCorporation/scimaenaga' + s.summary = 'SCIM Adapter for Rails.' + s.description = 'SCIM Adapter for Rails.' + s.license = 'MIT' - s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] + s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.md'] - s.required_ruby_version = ">= 2.5.9", "< 3.1" - s.add_dependency "rails", ">= 5.2.4.6", "< 6.2" - s.add_runtime_dependency "jwt", ">= 1.5" - s.test_files = Dir["spec/**/*"] + s.required_ruby_version = '>= 2.5.9', '< 3.1' + s.add_dependency 'rails', '>= 5.2.4.6', '< 6.2' + s.add_runtime_dependency 'jwt', '>= 1.5' + s.test_files = Dir['spec/**/*'] - s.add_development_dependency "bundler", "~> 2.0" - s.add_development_dependency "factory_bot_rails" - s.add_development_dependency "pry" - s.add_development_dependency "rake", "~> 13.0" - s.add_development_dependency "rspec-rails", "~> 5.0" - s.add_development_dependency "sqlite3", "~> 1.3", "< 1.5" + s.add_development_dependency 'bundler', '~> 2.0' + s.add_development_dependency 'factory_bot_rails' + s.add_development_dependency 'pry' + s.add_development_dependency 'rake', '~> 13.0' + s.add_development_dependency 'rspec-rails', '~> 5.0' + s.add_development_dependency 'sqlite3', '~> 1.3', '< 1.5' + s.metadata = { + 'rubygems_mfa_required' => 'true', + } end diff --git a/spec/controllers/scim_rails/scim_groups_controller_spec.rb b/spec/controllers/scim_rails/scim_groups_controller_spec.rb index f11ec456..72d1dd5d 100644 --- a/spec/controllers/scim_rails/scim_groups_controller_spec.rb +++ b/spec/controllers/scim_rails/scim_groups_controller_spec.rb @@ -417,8 +417,8 @@ Operations: [{ op: 'Replace', path: 'displayName', - value: 'changed' - }] + value: 'changed', + }], }, as: :json end.to change { group.reload.name }.to('changed') @@ -572,7 +572,7 @@ context 'whenr target Group is not found' do it 'return 404 not found' do expect do - delete :destroy, params: { id: 999999 }, as: :json + delete :destroy, params: { id: 999_999 }, as: :json end.not_to change { company.groups.reload.count }.from(1) expect(response.status).to eq 404 diff --git a/spec/controllers/scim_rails/scim_groups_request_spec.rb b/spec/controllers/scim_rails/scim_groups_request_spec.rb index be40bee1..6d9c58d1 100644 --- a/spec/controllers/scim_rails/scim_groups_request_spec.rb +++ b/spec/controllers/scim_rails/scim_groups_request_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "spec_helper" +require 'spec_helper' RSpec.describe ScimRails::ScimGroupsController, type: :request do let(:company) { create(:company) } @@ -9,34 +9,34 @@ end let(:authorization) { "Basic #{credentials}" } - def post_request(content_type = "application/scim+json") - post "/scim/v2/Groups", + def post_request(content_type = 'application/scim+json') + post '/scim/v2/Groups', params: { - displayName: "Dummy Group", - members: [] + displayName: 'Dummy Group', + members: [], }.to_json, headers: { Authorization: authorization, - 'Content-Type': content_type + 'Content-Type': content_type, } end - describe "Content-Type" do - it "accepts scim+json" do + describe 'Content-Type' do + it 'accepts scim+json' do expect(company.groups.count).to eq 0 - post_request("application/scim+json") + post_request('application/scim+json') expect(request.params).to include :displayName expect(response.status).to eq 201 - expect(response.media_type).to eq "application/scim+json" + expect(response.media_type).to eq 'application/scim+json' expect(company.groups.count).to eq 1 end - it "can not parse unfamiliar content types" do + it 'can not parse unfamiliar content types' do expect(company.groups.count).to eq 0 - post_request("text/csv") + post_request('text/csv') expect(request.params).not_to include :displayName expect(response.status).to eq 422 @@ -44,21 +44,21 @@ def post_request(content_type = "application/scim+json") end end - context "OAuth Bearer Authorization" do - context "with valid token" do + context 'OAuth Bearer Authorization' do + context 'with valid token' do let(:authorization) { "Bearer #{company.api_token}" } - it "supports OAuth bearer authorization and succeeds" do + it 'supports OAuth bearer authorization and succeeds' do expect { post_request }.to change(company.groups, :count).from(0).to(1) expect(response.status).to eq 201 end end - context "with invalid token" do + context 'with invalid token' do let(:authorization) { "Bearer #{SecureRandom.hex}" } - it "The request fails" do + it 'The request fails' do expect { post_request }.not_to change(company.groups, :count) expect(response.status).to eq 401 diff --git a/spec/controllers/scim_rails/scim_users_controller_spec.rb b/spec/controllers/scim_rails/scim_users_controller_spec.rb index 96235c61..b7af6a4a 100644 --- a/spec/controllers/scim_rails/scim_users_controller_spec.rb +++ b/spec/controllers/scim_rails/scim_users_controller_spec.rb @@ -755,23 +755,23 @@ describe 'destroy' do let(:company) { create(:company) } - context "when unauthorized" do - it "returns scim+json content type" do + context 'when unauthorized' do + it 'returns scim+json content type' do delete :destroy, params: { id: 1 }, as: :json - expect(response.media_type).to eq "application/scim+json" + expect(response.media_type).to eq 'application/scim+json' end - it "fails with no credentials" do + it 'fails with no credentials' do delete :destroy, params: { id: 1 }, as: :json expect(response.status).to eq 401 end - it "fails with invalid credentials" do - request.env["HTTP_AUTHORIZATION"] = + it 'fails with invalid credentials' do + request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic - .encode_credentials("unauthorized", "123456") + .encode_credentials('unauthorized', '123456') delete :destroy, params: { id: 1 }, as: :json @@ -787,7 +787,6 @@ end context 'when User destroy method is configured' do - it 'is sucessful with valid credentials' do delete :destroy, params: { id: 1 }, as: :json @@ -840,7 +839,7 @@ context 'when target User is not found' do it 'return 404 not found' do expect do - delete :destroy, params: { id: 999999 }, as: :json + delete :destroy, params: { id: 999_999 }, as: :json end.not_to change { company.users.reload.count }.from(1) expect(response.status).to eq 404 diff --git a/spec/controllers/scim_rails/scim_users_request_spec.rb b/spec/controllers/scim_rails/scim_users_request_spec.rb index 41a75c2e..7757866c 100644 --- a/spec/controllers/scim_rails/scim_users_request_spec.rb +++ b/spec/controllers/scim_rails/scim_users_request_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "spec_helper" +require 'spec_helper' RSpec.describe ScimRails::ScimUsersController, type: :request do let(:company) { create(:company) } @@ -9,43 +9,43 @@ end let(:authorization) { "Basic #{credentials}" } - def post_request(content_type = "application/scim+json") + def post_request(content_type = 'application/scim+json') # params need to be transformed into a string to test if they are being parsed by Rack - post "/scim/v2/Users", + post '/scim/v2/Users', params: { name: { - givenName: "New", - familyName: "User" + givenName: 'New', + familyName: 'User', }, emails: [ { - value: "new@example.com" + value: 'new@example.com', } - ] + ], }.to_json, headers: { Authorization: authorization, - 'Content-Type': content_type + 'Content-Type': content_type, } end - describe "Content-Type" do - it "accepts scim+json" do + describe 'Content-Type' do + it 'accepts scim+json' do expect(company.users.count).to eq 0 - post_request("application/scim+json") + post_request('application/scim+json') expect(request.params).to include :name expect(response.status).to eq 201 - expect(response.media_type).to eq "application/scim+json" + expect(response.media_type).to eq 'application/scim+json' expect(company.users.count).to eq 1 end - it "can not parse unfamiliar content types" do + it 'can not parse unfamiliar content types' do expect(company.users.count).to eq 0 - post_request("text/csv") + post_request('text/csv') expect(request.params).not_to include :name expect(response.status).to eq 422 @@ -53,21 +53,21 @@ def post_request(content_type = "application/scim+json") end end - context "OAuth Bearer Authorization" do - context "with valid token" do + context 'OAuth Bearer Authorization' do + context 'with valid token' do let(:authorization) { "Bearer #{company.api_token}" } - it "supports OAuth bearer authorization and succeeds" do + it 'supports OAuth bearer authorization and succeeds' do expect { post_request }.to change(company.users, :count).from(0).to(1) expect(response.status).to eq 201 end end - context "with invalid token" do + context 'with invalid token' do let(:authorization) { "Bearer #{SecureRandom.hex}" } - it "The request fails" do + it 'The request fails' do expect { post_request }.not_to change(company.users, :count) expect(response.status).to eq 401 diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index 5a768aa3..ab87e5bb 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -3,7 +3,7 @@ require 'rails/all' Bundler.require(*Rails.groups) -require "scim_rails" +require 'scim_rails' module Dummy class Application < Rails::Application @@ -12,4 +12,3 @@ class Application < Rails::Application # -- all .rb files in that directory are automatically loaded. end end - diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index cfcdb756..71a1c171 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -1,3 +1,3 @@ Rails.application.routes.draw do - mount ScimRails::Engine => "/" + mount ScimRails::Engine => '/' end diff --git a/spec/factories/company.rb b/spec/factories/company.rb index 0097c4c7..1d778596 100644 --- a/spec/factories/company.rb +++ b/spec/factories/company.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :company do - name { "Test Company" } - subdomain { "test" } + name { 'Test Company' } + subdomain { 'test' } after(:build) do |company| company.api_token = ScimRails::Encoder.encode(company) diff --git a/spec/lib/scim_rails/encoder_spec.rb b/spec/lib/scim_rails/encoder_spec.rb index 3a2b3fbb..fd1046ae 100644 --- a/spec/lib/scim_rails/encoder_spec.rb +++ b/spec/lib/scim_rails/encoder_spec.rb @@ -1,61 +1,63 @@ -require "spec_helper" +require 'spec_helper' describe ScimRails::Encoder do - let(:company) { Company.new(subdomain: "test") } + let(:company) { Company.new(subdomain: 'test') } - describe "::encode" do - context "with signing configuration" do - it "generates a signed token with the company attribute" do + describe '::encode' do + context 'with signing configuration' do + it 'generates a signed token with the company attribute' do token = ScimRails::Encoder.encode(company) payload = ScimRails::Encoder.decode(token) - expect(token).to match /[a-z|A-Z|0-9.]{16,}\.[a-z|A-Z|0-9.]{16,}/ - expect(payload).to contain_exactly(["iat", Integer], ["subdomain", "test"]) + expect(token).to match(/[a-z|A-Z0-9.]{16,}\.[a-z|A-Z0-9.]{16,}/) + expect(payload).to contain_exactly(['iat', Integer], %w[subdomain test]) end end - context "without signing configuration" do + context 'without signing configuration' do before do allow(ScimRails.config).to receive(:signing_secret).and_return(nil) allow(ScimRails.config).to receive(:signing_algorithm).and_return(ScimRails::Config::ALGO_NONE) end - it "generates an unsigned token with the company attribute" do + it 'generates an unsigned token with the company attribute' do token = ScimRails::Encoder.encode(company) payload = ScimRails::Encoder.decode(token) - expect(token).to match /[a-z|A-Z|0-9.]{16,}/ - expect(payload).to contain_exactly(["iat", Integer], ["subdomain", "test"]) + expect(token).to match(/[a-z|A-Z0-9.]{16,}/) + expect(payload).to contain_exactly(['iat', Integer], %w[subdomain test]) end end end - describe "::decode" do + describe '::decode' do let(:token) { ScimRails::Encoder.encode(company) } - it "raises InvalidCredentials error for an invalid token" do - token = "f487bf84bfub4f74fj4894fnh483f4h4u8f" - expect { ScimRails::Encoder.decode(token) }.to raise_error ScimRails::ExceptionHandler::InvalidCredentials + it 'raises InvalidCredentials error for an invalid token' do + token = 'f487bf84bfub4f74fj4894fnh483f4h4u8f' + expect do + ScimRails::Encoder.decode(token) + end.to raise_error ScimRails::ExceptionHandler::InvalidCredentials end - context "with signing configuration" do - it "decodes a signed token, returning the company attributes" do + context 'with signing configuration' do + it 'decodes a signed token, returning the company attributes' do payload = ScimRails::Encoder.decode(token) - expect(payload).to contain_exactly(["iat", Integer], ["subdomain", "test"]) + expect(payload).to contain_exactly(['iat', Integer], %w[subdomain test]) end end - context "without signing configuration" do + context 'without signing configuration' do before do allow(ScimRails.config).to receive(:signing_secret).and_return(nil) allow(ScimRails.config).to receive(:signing_algorithm).and_return(ScimRails::Config::ALGO_NONE) end - it "decodes an unsigned token, returning the company attributes" do + it 'decodes an unsigned token, returning the company attributes' do payload = ScimRails::Encoder.decode(token) - expect(payload).to contain_exactly(["iat", Integer], ["subdomain", "test"]) + expect(payload).to contain_exactly(['iat', Integer], %w[subdomain test]) end end end diff --git a/spec/models/scim_query_parser_spec.rb b/spec/models/scim_query_parser_spec.rb index 5fe3ca0c..8a62d39f 100644 --- a/spec/models/scim_query_parser_spec.rb +++ b/spec/models/scim_query_parser_spec.rb @@ -3,18 +3,17 @@ require 'spec_helper' describe ScimRails::ScimQueryParser do - let(:query_string) { 'userName eq "taro"' } - let(:queryable_attributes) { + let(:queryable_attributes) do { userName: :name, emails: [ { - value: :email + value: :email, } - ] + ], } - } + end let(:parser) { described_class.new(query_string, queryable_attributes) } describe '#attribute' do