diff --git a/.travis.yml b/.travis.yml index d3fc1c66..d763cc5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,17 +2,28 @@ language: ruby branches: only: - master -rvm: - - 2.3 -gemfile: - - gemfiles/rails_4_2_pundit_1.gemfile - - gemfiles/rails_5_0_pundit_1.gemfile - - gemfiles/rails_5_1_pundit_1.gemfile - - gemfiles/rails_5_2_pundit_1.gemfile - - gemfiles/rails_4_2_pundit_2.gemfile - - gemfiles/rails_5_0_pundit_2.gemfile - - gemfiles/rails_5_1_pundit_2.gemfile - - gemfiles/rails_5_2_pundit_2.gemfile +jobs: + include: + - rvm: 2.3 + gemfile: gemfiles/rails_4_2_pundit_1.gemfile + - rvm: 2.3 + gemfile: gemfiles/rails_4_2_pundit_2.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_5_0_pundit_1.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_5_0_pundit_2.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_5_1_pundit_1.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_5_1_pundit_2.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_5_2_pundit_1.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_5_2_pundit_2.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_6_0_pundit_1.gemfile + - rvm: 2.5 + gemfile: gemfiles/rails_6_0_pundit_2.gemfile before_install: - gem install bundler -v '< 2' notifications: diff --git a/Appraisals b/Appraisals index 795a6f45..4d583e12 100644 --- a/Appraisals +++ b/Appraisals @@ -1,47 +1,97 @@ appraise 'rails-4-2 pundit-1' do gem 'rails', '4.2.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 1.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end end appraise 'rails-5-0 pundit-1' do gem 'rails', '5.0.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 1.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end end appraise 'rails-5-1 pundit-1' do gem "rails", "5.1.0" - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 1.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end end appraise 'rails-5-2 pundit-1' do gem 'rails', '5.2.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 1.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end +end + +appraise 'rails-6-0 pundit-1' do + gem 'rails', '~> 6.0.0' + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' + gem 'pundit', '~> 1.0' + group :development, :test do + gem 'sqlite3', '~> 1.4.1' + end end appraise 'rails-4-2 pundit-2' do gem 'rails', '4.2.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 2.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end end appraise 'rails-5-0 pundit-2' do gem 'rails', '5.0.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 2.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end end appraise 'rails-5-1 pundit-2' do gem 'rails', '5.1.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 2.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end end appraise 'rails-5-2 pundit-2' do gem 'rails', '5.2.0' - gem 'jsonapi-resources', '~> 0.9.0' + # ToDo: This is only for testing purposes + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' + gem 'pundit', '~> 2.0' + group :development, :test do + gem 'sqlite3', '~> 1.3.13' + end +end + +appraise 'rails-6-0 pundit-2' do + gem 'rails', '~> 6.0.0' + gem 'jsonapi-resources', :git => 'https://github.com/cerebris/jsonapi-resources.git', :branch => 'use_records_for_joined_resources' gem 'pundit', '~> 2.0' + group :development, :test do + gem 'sqlite3', '~> 1.4.1' + end end diff --git a/Gemfile b/Gemfile index fa75df15..580f8e18 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,10 @@ source 'https://rubygems.org' +# TODO: This is only for testing purposes +gem( + 'jsonapi-resources', + git: 'https://github.com/cerebris/jsonapi-resources.git', + branch: 'use_records_for_joined_resources' +) + gemspec diff --git a/README.md b/README.md index c2d7b8b3..ea608f5a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # JSONAPI::Authorization -[![Build Status](https://img.shields.io/travis/venuu/jsonapi-authorization/master.svg?style=flat&maxAge=3600)](https://travis-ci.org/venuu/jsonapi-authorization) [![Gem Version](https://img.shields.io/gem/v/jsonapi-authorization.svg?style=flat&maxAge=3600)](https://rubygems.org/gems/jsonapi-authorization) +[![Build Status](https://img.shields.io/travis/com/venuu/jsonapi-authorization/master.svg?style=flat&maxAge=3600)](https://travis-ci.com/venuu/jsonapi-authorization) [![Gem Version](https://img.shields.io/gem/v/jsonapi-authorization.svg?style=flat&maxAge=3600)](https://rubygems.org/gems/jsonapi-authorization) **NOTE:** This README is the documentation for `JSONAPI::Authorization`. If you are viewing this at the [project page on Github](https://github.com/venuu/jsonapi-authorization) you are viewing the documentation for the `master` diff --git a/gemfiles/rails_4_2_pundit_1.gemfile b/gemfiles/rails_4_2_pundit_1.gemfile index ed81ddfa..781d65d9 100644 --- a/gemfiles/rails_4_2_pundit_1.gemfile +++ b/gemfiles/rails_4_2_pundit_1.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "4.2.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 1.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_4_2_pundit_2.gemfile b/gemfiles/rails_4_2_pundit_2.gemfile index 9ce4476b..bde79232 100644 --- a/gemfiles/rails_4_2_pundit_2.gemfile +++ b/gemfiles/rails_4_2_pundit_2.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "4.2.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 2.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_0_pundit_1.gemfile b/gemfiles/rails_5_0_pundit_1.gemfile index c53a19d7..9f042671 100644 --- a/gemfiles/rails_5_0_pundit_1.gemfile +++ b/gemfiles/rails_5_0_pundit_1.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "5.0.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 1.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_0_pundit_2.gemfile b/gemfiles/rails_5_0_pundit_2.gemfile index 253e51f7..b2c632f5 100644 --- a/gemfiles/rails_5_0_pundit_2.gemfile +++ b/gemfiles/rails_5_0_pundit_2.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "5.0.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 2.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_1_pundit_1.gemfile b/gemfiles/rails_5_1_pundit_1.gemfile index 9e28ecea..8e753452 100644 --- a/gemfiles/rails_5_1_pundit_1.gemfile +++ b/gemfiles/rails_5_1_pundit_1.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "5.1.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 1.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_1_pundit_2.gemfile b/gemfiles/rails_5_1_pundit_2.gemfile index 3305a4bb..f106ebc1 100644 --- a/gemfiles/rails_5_1_pundit_2.gemfile +++ b/gemfiles/rails_5_1_pundit_2.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "5.1.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 2.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_2_pundit_1.gemfile b/gemfiles/rails_5_2_pundit_1.gemfile index 55f876dd..081b9e38 100644 --- a/gemfiles/rails_5_2_pundit_1.gemfile +++ b/gemfiles/rails_5_2_pundit_1.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "5.2.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 1.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_2_pundit_2.gemfile b/gemfiles/rails_5_2_pundit_2.gemfile index 5c948725..a3f0db0c 100644 --- a/gemfiles/rails_5_2_pundit_2.gemfile +++ b/gemfiles/rails_5_2_pundit_2.gemfile @@ -2,8 +2,12 @@ source "https://rubygems.org" +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" gem "rails", "5.2.0" -gem "jsonapi-resources", "~> 0.9.0" gem "pundit", "~> 2.0" +group :development, :test do + gem "sqlite3", "~> 1.3.13" +end + gemspec path: "../" diff --git a/gemfiles/rails_6_0_pundit_1.gemfile b/gemfiles/rails_6_0_pundit_1.gemfile new file mode 100644 index 00000000..4d9e6f31 --- /dev/null +++ b/gemfiles/rails_6_0_pundit_1.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" +gem "rails", "~> 6.0.0" +gem "pundit", "~> 1.0" + +group :development, :test do + gem "sqlite3", "~> 1.4.1" +end + +gemspec path: "../" diff --git a/gemfiles/rails_6_0_pundit_2.gemfile b/gemfiles/rails_6_0_pundit_2.gemfile new file mode 100644 index 00000000..2c4989b6 --- /dev/null +++ b/gemfiles/rails_6_0_pundit_2.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "jsonapi-resources", git: "https://github.com/cerebris/jsonapi-resources.git", branch: "use_records_for_joined_resources" +gem "rails", "~> 6.0.0" +gem "pundit", "~> 2.0" + +group :development, :test do + gem "sqlite3", "~> 1.4.1" +end + +gemspec path: "../" diff --git a/jsonapi-authorization.gemspec b/jsonapi-authorization.gemspec index d2a3c6d3..17354a1e 100644 --- a/jsonapi-authorization.gemspec +++ b/jsonapi-authorization.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |spec| spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.require_paths = ["lib"] - spec.add_dependency "jsonapi-resources", "~> 0.9.0" + spec.add_dependency "jsonapi-resources", "~> 0.10.0" spec.add_dependency "pundit", ">= 1.0.0", "< 3.0.0" spec.add_development_dependency "appraisal" @@ -31,5 +31,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "pry-rails" spec.add_development_dependency "rubocop", "~> 0.36.0" spec.add_development_dependency "phare", "~> 0.7.1" - spec.add_development_dependency "sqlite3", "~> 1.3.6" + spec.add_development_dependency "sqlite3", "~> 1.3" end diff --git a/lib/jsonapi/authorization/authorizing_processor.rb b/lib/jsonapi/authorization/authorizing_processor.rb index 4728bb44..0b102751 100644 --- a/lib/jsonapi/authorization/authorizing_processor.rb +++ b/lib/jsonapi/authorization/authorizing_processor.rb @@ -72,7 +72,7 @@ def authorize_show_relationship related_resource = case relationship when JSONAPI::Relationship::ToOne - parent_resource.public_send(params[:relationship_type].to_sym) + resources_from_relationship(source_klass, source_id, relationship.type, context).first when JSONAPI::Relationship::ToMany # Do nothing — already covered by policy scopes else @@ -91,7 +91,9 @@ def authorize_show_related_resource source_resource = source_klass.find_by_key(source_id, context: context) - related_resource = source_resource.public_send(relationship_type) + related_resource = resources_from_relationship( + source_klass, source_id, relationship_type, context + ).first source_record = source_resource._model related_record = related_resource._model unless related_resource.nil? @@ -282,6 +284,16 @@ def authorizer @authorizer ||= ::JSONAPI::Authorization.configuration.authorizer.new(context: context) end + def resources_from_relationship(source_klass, source_id, relationship_type, context) + rid = source_klass.find_related_fragments( + [JSONAPI::ResourceIdentity.new(source_klass, source_id)], + relationship_type, + context: context + ).keys.first + + rid.resource_klass.find_to_populate_by_keys(rid.id) + end + # TODO: Communicate with upstream to fix this nasty hack def operation_resource_id case operation_type diff --git a/lib/jsonapi/authorization/pundit_scoped_resource.rb b/lib/jsonapi/authorization/pundit_scoped_resource.rb index 5166eadd..611e390b 100644 --- a/lib/jsonapi/authorization/pundit_scoped_resource.rb +++ b/lib/jsonapi/authorization/pundit_scoped_resource.rb @@ -8,35 +8,7 @@ module PunditScopedResource module ClassMethods def records(options = {}) user_context = JSONAPI::Authorization.configuration.user_context(options[:context]) - ::Pundit.policy_scope!(user_context, _model_class) - end - end - - def records_for(association_name) - record_or_records = @model.public_send(association_name) - relationship = fetch_relationship(association_name) - - case relationship - when JSONAPI::Relationship::ToOne - record_or_records - when JSONAPI::Relationship::ToMany - user_context = JSONAPI::Authorization.configuration.user_context(context) - ::Pundit.policy_scope!(user_context, record_or_records) - else - raise "Unknown relationship type #{relationship.inspect}" - end - end - - private - - def fetch_relationship(association_name) - relationships = self.class._relationships.select do |_k, v| - v.relation_name(context: context) == association_name - end - if relationships.empty? - nil - else - relationships.values.first + ::Pundit.policy_scope!(user_context, super) end end end diff --git a/spec/dummy/app/assets/config/manifest.js b/spec/dummy/app/assets/config/manifest.js new file mode 100644 index 00000000..e69de29b diff --git a/spec/dummy/app/controllers/application_controller.rb b/spec/dummy/app/controllers/application_controller.rb new file mode 100644 index 00000000..09705d12 --- /dev/null +++ b/spec/dummy/app/controllers/application_controller.rb @@ -0,0 +1,2 @@ +class ApplicationController < ActionController::Base +end diff --git a/spec/dummy/app/controllers/articles_controller.rb b/spec/dummy/app/controllers/articles_controller.rb index 5dc4da9d..00cc4c48 100644 --- a/spec/dummy/app/controllers/articles_controller.rb +++ b/spec/dummy/app/controllers/articles_controller.rb @@ -1,4 +1,4 @@ -class ArticlesController < ActionController::Base +class ArticlesController < ApplicationController include JSONAPI::ActsAsResourceController rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized diff --git a/spec/dummy/app/controllers/comments_controller.rb b/spec/dummy/app/controllers/comments_controller.rb index 5f4fa27f..0f2d7b2e 100644 --- a/spec/dummy/app/controllers/comments_controller.rb +++ b/spec/dummy/app/controllers/comments_controller.rb @@ -1,4 +1,4 @@ -class CommentsController < ActionController::Base +class CommentsController < ApplicationController include JSONAPI::ActsAsResourceController rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized diff --git a/spec/dummy/app/controllers/tags_controller.rb b/spec/dummy/app/controllers/tags_controller.rb index 4b6a4312..388079b9 100644 --- a/spec/dummy/app/controllers/tags_controller.rb +++ b/spec/dummy/app/controllers/tags_controller.rb @@ -1,4 +1,4 @@ -class TagsController < ActionController::Base +class TagsController < ApplicationController include JSONAPI::ActsAsResourceController rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized diff --git a/spec/dummy/app/controllers/users_controller.rb b/spec/dummy/app/controllers/users_controller.rb index 9ad91d46..f81ae390 100644 --- a/spec/dummy/app/controllers/users_controller.rb +++ b/spec/dummy/app/controllers/users_controller.rb @@ -1,4 +1,4 @@ -class UsersController < ActionController::Base +class UsersController < ApplicationController include JSONAPI::ActsAsResourceController rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized diff --git a/spec/support/custom_matchers.rb b/spec/support/custom_matchers.rb new file mode 100644 index 00000000..8a2f9681 --- /dev/null +++ b/spec/support/custom_matchers.rb @@ -0,0 +1,54 @@ +# Add better debuggability to be_forbidden failures +RSpec::Matchers.define :be_forbidden do + match(&:forbidden?) + + failure_message do |actual| + debug_text_for_failure('forbidden', response: actual, last_request: last_request) + end +end + +# Add better debuggability to be_not_found failures +RSpec::Matchers.define :be_not_found do + match(&:not_found?) + + failure_message do |actual| + debug_text_for_failure('not_found', response: actual, last_request: last_request) + end +end + +# Add better debuggability to be_unprocessable failures +RSpec::Matchers.define :be_unprocessable do + match(&:unprocessable?) + + failure_message do |actual| + debug_text_for_failure('unprocessable', response: actual, last_request: last_request) + end +end + +# Add better debuggability to be_successful failures +RSpec::Matchers.define :be_successful do + match(&:successful?) + + failure_message do |actual| + debug_text_for_failure('successful', response: actual, last_request: last_request) + end +end + +# Add better debuggability to be_ok failures +RSpec::Matchers.define :be_ok do + match(&:ok?) + + failure_message do |actual| + debug_text_for_failure('ok', response: actual, last_request: last_request) + end +end + +def debug_text_for_failure(expected, response:, last_request:) + debug_text = "expected response to be #{expected} but HTTP code was #{response.status}." + debug_text += " Last request was #{last_request.request_method} to #{last_request.fullpath}" + unless last_request.get? + debug_text += " with body:\n" + last_request.body.read + end + debug_text += "\nResponse body was:\n" + response.body + debug_text +end