diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 00000000..0d6fc505 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,5 @@ +version: "2" +checks: + argument-count: + config: + threshold: 5 diff --git a/.travis.yml b/.travis.yml index fe7bb807..f85fdf19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,10 @@ language: ruby cache: bundler sudo: false rvm: - - 2.2.8 - 2.3.5 - 2.4.2 - 2.5.0 + - 2.5.1 - jruby-9.0.5.0 - jruby-9.1.9.0 gemfile: @@ -18,14 +18,6 @@ services: matrix: fast_finish: true exclude: - - rvm: 2.2.0 - gemfile: gemfiles/activerecord_5.0.2.gemfile - - rvm: 2.2.0 - gemfile: gemfiles/activerecord_5.1.0.gemfile - - rvm: 2.2.7 - gemfile: gemfiles/activerecord_5.0.2.gemfile - - rvm: 2.2.7 - gemfile: gemfiles/activerecord_5.1.0.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/activerecord_5.0.2.gemfile - rvm: jruby-9.1.9.0 @@ -42,7 +34,6 @@ notifications: email: recipients: - alessandro.rodi@renuo.ch - - josua.schmid@renuo.ch on_success: change on_failure: change before_install: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..5070c3df --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,617 @@ +## 2.3.0 (Sep 16th, 2018) + +* [#528](https://github.com/CanCanCommunity/cancancan/issues/528): Compress irrelevant rules before generating a query to optimize performances. ([@coorasse][]) +* [#529](https://github.com/CanCanCommunity/cancancan/issues/529): Remove ruby 2.2 from Travis and add ruby 2.5.1. ([@coorasse][]) +* [#530](https://github.com/CanCanCommunity/cancancan/issues/530): Predict associations names to support multiple references to the same table. ([@coorasse][]) +* [#530](https://github.com/CanCanCommunity/cancancan/issues/530): Raise a specific exception when using a wrong association name in rules definition. ([@coorasse][]) + +## 2.2.0 (Apr 13th, 2018) + +* [#482](https://github.com/CanCanCommunity/cancancan/issues/482): Include conditions passed to authorize! in AccessDenied exception. ([@kraflab][]) +* Removed support for dynamic finders. ([@coorasse][]) +* [#479](https://github.com/CanCanCommunity/cancancan/issues/479): Support Rails 5.2. ([@lizzyaustad][]) +* Use ActiveSupport standard loader. ([@BookOfGreg][]) + +## 2.1.4 (Apr 09th, 2018) + +* Inject cancancan in ActionController::API and ActionController::Base when they are both defined. ([@arturoherrero][]) + +## 2.1.3 (Jan 16th, 2018) + +* Fix compatibility with Rails 5 API. ([@Eric-Guo][]) + +## 2.1.2 (Nov 22th, 2017) + +* Various bugfixes on version 2.1.0. ([@coorasse][]) + +## 2.1.0 (Nov 10th, 2017) + +* Adds support for Rails Api applications. ([@ajgon][]) +* Controller subclasses inherit skip_load_resource from superclass. ([@jpmckinney][]) +* Fix instance variable not initialized warnings. ([@sethcharles][]) +* Fix build_resource when model name is Action. ([@anilmaurya][]) +* Smaller performance improvements. ([@DNNX][]) +* Fix i18n lookup for unauthorized message. ([@clemens][]) + +## 2.0.0 (May 18th, 2017) + +* Drop support for Rails < 4.2. ([@oliverklee][]) +* Drop support for ruby < 2.2. ([@coorasse][]) +* Drop support for InheritedResource. ([@coorasse][]) +* Drop support for Sequel. ([@coorasse][]) +* Drop support for Mongoid. ([@coorasse][]) +* Add ability to rspec matcher to take array of abilities. ([@gingray][]) +* [#204](https://github.com/CanCanCommunity/cancancan/pull/204): Increase Performance. ([@timraymond][]) +* Removed controller methods: skip_authorization, unauthorized!. ([@coorasse][]) +* Removed options: nested, name, resource. ([@coorasse][]) + +## 1.17.0 (March 26th, 2017) + +* Improve performance for the Mongoid Adapter. + + +## 1.16.0 (February 2nd, 2017) + +* Introduce rubocop and fixes most of the issues ([@coorasse][]). + +## 1.15.0 (June 13th, 2016) + +* Add support for Rails 5 (craig1410). + +## 1.14.0 (May 14th, 2016) + +* Use cover for ranges. +* Add support for rails 4 enum's (markpmitchell). + +## 1.13.1 (Oct 8th, 2015) + +* Fix #merge with empty Ability (jhawthorn). + +## 1.13.0 (Oct 7th, 2015) + +* Significantly improve rule lookup time (amarshall). +* Removed deprecation warnings for RSpec 3.2 (NekoNova). +* Drop support for REE and Ruby 1.x and so Rails 2 (Richard Wilson). + +## 1.12.0 (June 28th, 2015) + +* Add a permissions method to Ability (devaroop). + +## 1.11.0 (June 15th, 2015) + +* Complete cancancan#115 - Specify authorization action for parent resources. (phallguy). + +## 1.10.1 (January 13th, 2015) + +* Fix cancancan#168 - A bug with ActiveRecord 4.2 support causing ProtocolViolation due to named parameters not being passed in. + + +## 1.10.0 (January 7th, 2015) + +* Fix i18n issue for Ruby < 1.9.3 ([@bryanrite][]). + +* Fix cancancan#149 - Fix an issue loading namespaced models (darthjee). + +* Fix cancancan#160 - Support for Rails 4.2 (marshall-lee). + +* Fix cancancan#153 - More useful output in ability spec matchers (jondkinney). + + +## 1.9.2 (August 8th, 2014) + +* Fix cancancan#77, 78 - Fix an issue with associations for namespaced models (jjp). + + +## 1.9.1 (July 21st, 2014) + +* Fix cancancan#101 - Fixes an issue where overjealous use of references would cause issues with scopes when loading associations ([@bryanrite][]). + + +## 1.9.0 (July 20th, 2014) + +* Fix cancancan#59 - Parameters are automatically detected and santitized for all actions, not just create and update ([@bryanrite][]). + +* Fix cancancan#97, 72, 40, 39, 26 - Support Active Record 4 properly with references on nested permissions (scpike, tdg5, Crystark). + + +## 1.8.4 (June 24th, 2014) + +* Fix cancancan#86 - Fixes previous RSpec 3 update as there was a bug in the fix for RSpec 2.99 ([@bryanrite][]). + + +## 1.8.3 (June 24th, 2014) + +* Fix cancancan#85 - Remove deprecation notices for RSpec 3 and continue backwards compatibility (andypike, bryanrite, porteta). + + +## 1.8.2 (June 5th, 2014) + +* Fix cancancan#75 - More specific hash-like object check. ([@bryanrite][]). + + +## 1.8.1 (May 27th, 2014) + +* Fix cancancan#67 - Sequel tests are run properly for JRuby. ([@bryanrite][]). + +* Fix cancancan#68 - Checks for hash-like objects in subject better. ([@bryanrite][]). + + +## 1.8.0 (May 8th, 2014) + +* Feature cancan#884 - Add a Sequel model adapter (szetobo). + +* Feature cancancan#3 - Permit "can?" check multiple subjects (cefigueiredo). + +* Feature cancancan#29 - Add ability to use a String that will get instance_eval'd or a Proc that will get called as the parameter method option for strong_parameter santization (svoop). + +* Feature cancancan#48 - Define a CanCanCan module. Even though it is not used, it is standard practice to define the module, and helpful for determining between CanCanCan and CanCan for external libraries. + + +## 1.7.1 (March 19th, 2014) + +* Fix ryanb/cancan#992 - Remove Rails 4 deprecations for scoped (thejchap & hitendrasingh). + +* Fix cancancan#16 - RSpec expectations are not explicitly required in RSpec > 2.13 (justinaiken & bryanrite). + + +## 1.7.0 (February 19th, 2014) + +* Feature #988 Adds support for strong_parameters ([@bryanrite][]). + +* Fix #726 - Allow multiple abilities with associations (elabs-dev). + +* Fix #864 - Fix id_param in shallow routes (francocatena). + +* Fix #871 - Fixes nested ability conditions (ricec). + +* Fix #935 - Reduce unnecessary object allocations (grosser). + +* Fix #966 - Fixes a variable name collision in nested conditions (knoopx). + +* Fix #971 - Does not execute "empty?" scope when checking class rule (matt-glover). + +* Fix #974 - Avoid unnecessary sql execution (inkstak). + + +## 1.6.10 (May 7, 2013) + +* Fix matches_conditons_hash for string values on 1.8 ([@rrosen][]). + +* Work around SQL injection vulnerability in older Rails versions ([@steerio][]) - issue #800. + +* Add support for nested join conditions ([@yuszuv][]) - issue #806. + +* Fix load_resource "find_by" in mongoid resources ([@albertobajo][]) - issue #705. + +* Fix namespace split behavior ([@xinuc][]) - issue #668. + + +## 1.6.9 (February 4, 2013) + +* Fix inserting AND (NULL) to end of SQL queries (jonsgreen) - issue #687. + +* Fix merge_joins for nested association hashes (DavidMikeSimon) - issues #655, #560. + +* Raise error on recursive alias_action (fl00r) - issue #660. + +* Fix namespace controllers not loading params (andhapp) - issues #670, #664. + + +## 1.6.8 (June 25, 2012) + +* Improved support for namespaced controllers and models. + +* Pass :if and :unless options for load and authorize resource (mauriciozaffari). + +* Travis CI badge (plentz). + +* Adding Ability#merge for combining multiple abilities (rogercampos). + +* Support for multiple MetaWhere rules (andhapp). + +* Various fixes for DataMapper, Mongoid, and Inherited Resource integration. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.7...1.6.8]. + + +## 1.6.7 (October 4, 2011) + +* Fixing nested resource problem caused by namespace addition - issue #482. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.6...1.6.7]. + + +## 1.6.6 (September 28, 2011) + +* Correct "return cant jump across threads" error when using check_authorization (codeprimate) - issues #463, #469. + +* Fixing tests in development by specifying with_model version (kirkconnell) - issue #476. + +* Added travis.yml file for TravisCI support (bai) - issue #427. + +* Better support for namespaced models (whilefalse) - issues #424. + +* Adding :id_param option to load_and_authorize_resource (skhisma) - issue #425. + +* Make default unauthorized message translatable text (nhocki) - issue #409. + +* Improving DataMapper behavior (psanford, maxsum-corin) - issue #410, #373. + +* Allow :find_by option to be full find method name - issue #335. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.5...1.6.6]. + + +## 1.6.5 (May 18, 2011) + +* [#366](https://github.com/CanCanCommunity/cancancan/issues/366): Pass action and subject through AccessDenied exception when :through isn't found. + +* Many Mongoid adapter improvements (rahearn, cardagin) - issues #363, #352, #343. + +* [#360](https://github.com/CanCanCommunity/cancancan/issues/360): Allow :through option to work with private controller methods. + +* [#359](https://github.com/CanCanCommunity/cancancan/issues/359): Ensure Mongoid::Document is defined before loading Mongoid adapter. + +* [#355](https://github.com/CanCanCommunity/cancancan/issues/355): Many DataMapper adapter improvements ([@emmanuel][]). + +* [#330](https://github.com/CanCanCommunity/cancancan/issues/330): Handle checking nil attributes through associations ([@thatothermitch][]). + +* Improve scope merging - issue #328. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.4...1.6.5]. + + +## 1.6.4 (March 29, 2011) + +* Fixed mongoid 'or' error - see issue #322. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.3...1.6.4]. + + +## 1.6.3 (March 25, 2011) + +* Make sure ActiveRecord::Relation is defined before checking conditions against it so Rails 2 is supported again - see issue #312. + +* Return subject passed to authorize! - see issue #314. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.2...1.6.3]. + + +## 1.6.2 (March 18, 2011) + +* Fixed instance loading when :singleton option is used - see issue #310. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.1...1.6.2]. + + +## 1.6.1 (March 15, 2011) + +* Use Item.new instead of build_item for singleton resource so it doesn't effect database - see issue #304. + +* Made accessible_by action default to :index and parent action default to :show instead of :read - see issue #302. + +* Reverted Inherited Resources "collection" override since it doesn't seem to be working - see issue #305. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.6.0...1.6.1]. + + +## 1.6.0 (March 11, 2011) + +* Added MetaWhere support - see issue #194 and #261. + +* Allow Active Record scopes in Ability conditions - see issue #257. + +* Added :if and :unless options to check_authorization - see issue #284. + +* Several Inherited Resources fixes (aq1018, tanordheim and stefanoverna). + +* Pass action name to accessible_by call when loading a collection ([@amw][]). + +* Added :prepend option to load_and_authorize_resource to load before other filters - see issue #290. + +* Fixed spacing issue in I18n message for multi-word model names - see issue #292. + +* Load resource collection for any action which doesn't have an "id" parameter - see issue #296. + +* Raise an exception when trying to make a Ability condition with both a hash of conditions and a block - see issue #269. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.5.1...1.6.0]. + + +## 1.5.1 (January 20, 2011) + +* Fixing deeply nested conditions in Active Record adapter - see issue #246. + +* Improving Mongoid support for multiple can and cannot definitions ([@stellard][]) - see issue #239. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.5.0...1.5.1]. + + +## 1.5.0 (January 11, 2011) + +* Added an Ability generator - see issue #170. + +* Added DataMapper support ([@natemueller][]). + +* Added Mongoid support ([@bowsersenior][]). + +* Added skip_load_and_authorize_resource methods to controller class - see issue #164. + +* Added support for uncountable resources in index action - see issue #193. + +* Cleaned up README and added spec/README. + +* Internal: renamed CanDefinition to Rule. + +* Internal: added a model adapter layer for easily supporting more ORMs. + +* Internal: added .rvmrc to auto-switch to 1.8.7 with gemset - see issue #231. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.4.1...1.5.0]. + + +## 1.4.1 (November 12, 2010) + +* Renaming skip_authorization to skip_authorization_check - see issue #169. + +* Adding :through_association option to load_resource ([@hunterae][]) - see issue #171. + +* The :shallow option now works with the :singleton option ([@nandalopes][]) - see issue #187. + +* Play nicely with quick_scopes gem ([@ramontayag][]) - see issue #183. + +* Fix odd behavior when "cache_classes = false" ([@mphalliday][]) - see issue #174. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.4.0...1.4.1]. + + +## 1.4.0 (October 5, 2010) + +* Adding Gemfile; to get specs running just +bundle+ and +rake+ - see issue #163. + +* Stop at 'cannot' definition when there are no conditions - see issue #161. + +* The :through option will now call a method with that name if instance variable doesn't exist - see issue #146. + +* Adding :shallow option to load_resource to bring back old behavior of fetching a child without a parent. + +* Raise AccessDenied error when loading a child and parent resource isn't found. + +* Abilities defined on a module will apply to anything that includes that module - see issue #150 and #152. + +* Abilities can be defined with a string of SQL in addition to a block so accessible_by works with a block - see issue #150. + +* Adding better support for InheritedResource - see issue #23. + +* Loading the collection instance variable (for index action) using accessible_by - see issue #137. + +* Adding action and subject variables to I18n unauthorized message - closes #142. + +* Adding check_authorization and skip_authorization controller class methods to ensure authorization is performed ([@justinko][]) - see issue #135. + +* Setting initial attributes based on ability conditions in new/create actions - see issue #114. + +* Check parent attributes for nested association in index action - see issue #121. + +* Supporting nesting in can? method using hash - see issue #121. + +* Adding I18n support for Access Denied messages ([@EppO][]) - see issue #103. + +* Passing no arguments to +can+ definition will pass action, class, and object to block - see issue #129. + +* Don't pass action to block in +can+ definition when using :+manage+ option - see issue #129. + +* No longer calling block in +can+ definition when checking on class - see issue #116. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.3.4...1.4.0]. + + +## 1.3.4 (August 31, 2010) + +* Don't stop at +cannot+ with hash conditions when checking class ([@tamoya][]) - see issue #131. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.3.3...1.3.4]. + + +## 1.3.3 (August 20, 2010) + +* Switching to Rspec namespace to remove deprecation warning in Rspec 2 - see issue #119. + +* Pluralize nested associations for conditions in accessible_by ([@mlooney][]) - see issue #123. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.3.2...1.3.3]. + + +## 1.3.2 (August 7, 2010) + +* Fixing slice error when passing in custom resource name - see issue #112. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.3.1...1.3.2]. + + +## 1.3.1 (August 6, 2010) + +* Fixing protected sanitize_sql error - see issue #111. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.3.0...1.3.1]. + + +## 1.3.0 (August 6, 2010) + +* Adding :find_by option to load_resource - see issue #19. + +* Adding :singleton option to load_resource - see issue #93. + +* Supporting multiple resources in :through option for polymorphic associations - see issue #73. + +* Supporting Single Table Inheritance for "can" comparisons - see issue #55. + +* Adding :instance_name option to load/authorize_resource - see issue #44. + +* Don't pass nil to "new" to keep MongoMapper happy - see issue #63. + +* Parent resources are now authorized with :read action. + +* Changing :resource option in load/authorize_resource back to :class with ability to pass false. + +* Removing :nested option in favor of :through option with separate load/authorize call. + +* Moving internal logic from ResourceAuthorization to ControllerResource class. + +* Supporting multiple "can" and "cannot" calls with accessible_by (funny-falcon) - see issue #71. + +* Supporting deeply nested aliases - see issue #98. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.2.0...1.3.0]. + + +## 1.2.0 (July 16, 2010) + +* Load nested parent resources on collection actions such as "index" (dohzya). + +* Adding :name option to load_and_authorize_resource if it does not match controller - see issue #65. + +* Fixing issue when using accessible_by with nil can conditions (jrallison) - see issue #66. + +* Pluralize table name for belongs_to associations in can conditions hash (logandk) - see issue #62. + +* Support has_many association or arrays in can conditions hash. + +* Adding joins clause to accessible_by when conditions are across associations. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.1.1...1.2.0]. + + +## 1.1.1 (April 17, 2010) + +* Fixing behavior in Rails 3 by properly initializing ResourceAuthorization. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.1...1.1.1]. + + +## 1.1.0 (April 17, 2010) + +* Supporting arrays, ranges, and nested hashes in ability conditions. + +* Removing "unauthorized!" method in favor of "authorize!" in controllers. + +* Adding action, subject and default_message abilities to AccessDenied exception - see issue #40. + +* Adding caching to current_ability controller method, if you're overriding this be sure to add caching too. + +* Adding "accessible_by" method to Active Record for fetching records matching a specific ability. + +* Adding conditions behavior to Ability#can and fetch with Ability#conditions - see issue #53. + +* Renaming :class option to :resource for load_and_authorize_resource which now supports a symbol for non models - see issue #45. + +* Properly handle Admin::AbilitiesController in params[:controller] - see issue #46. + +* Adding be_able_to RSpec matcher (dchelimsky), requires Ruby 1.8.7 or higher - see issue #54. + +* Support additional arguments to can? which get passed to the block - see issue #48. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.0.2...1.1]. + + +## 1.0.2 (Dec 30, 2009) + +* Adding clear_aliased_actions to Ability which removes previously defined actions including defaults - see issue #20. + +* Append aliased actions (don't overwrite them) - see issue #20. + +* Adding custom message argument to unauthorized! method (tjwallace) - see issue #18. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.0.1...1.0.2]. + + +## 1.0.1 (Dec 14, 2009) + +* Adding :class option to load_resource so one can customize which class to use for the model - see issue #17. + +* Don't fetch parent of nested resource if *_id parameter is missing so it works with shallow nested routes - see issue #14. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/1.0.0...1.0.1]. + + +## 1.0.0 (Dec 13, 2009) + +* Don't set resource instance variable if it has been set already - see issue #13. + +* Allowing :nested option to accept an array for deep nesting. + +* Adding :nested option to load resource method - see issue #10. + +* Pass :only and :except options to before filters for load/authorize resource methods. + +* Adding :collection and :new options to load_resource method so we can specify behavior of additional actions if needed. + +* BACKWARDS INCOMPATIBLE: turning load and authorize resource methods into class methods which set up the before filter so they can accept additional arguments. + +* {see the full list of changes}[https://github.com/CanCanCommunity/cancancan/compare/0.2.1...1.0.0]. + + +## 0.2.1 (Nov 26, 2009) + +* Many internal refactorings - see issues #11 and #12. + +* Adding "cannot" method to define which abilities cannot be done - see issue #7. + +* Support custom objects (usually symbols) in can definition - see issue #8. + +* See the full list of changes [https://github.com/CanCanCommunity/cancancan/compare/0.2.0...0.2.1]. + + +## 0.2.0 (Nov 17, 2009) + +* Fix behavior of load_and_authorize_resource for namespaced controllers - see issue #3. + +* Support arrays being passed to "can" to specify multiple actions or classes - see issue #2. + +* Adding "cannot?" method to ability, controller, and view which is inverse of "can?" - see issue #1. + +* BACKWARDS INCOMPATIBLE: use Ability#initialize instead of 'prepare' to set up abilities - see issue #4. + +* See the full list of changes [https://github.com/CanCanCommunity/cancancan/compare/0.1.0...0.2.0]. + + +## 0.1.0 (Nov 16, 2009) + +* Initial release. + +[@coorasse]: https://github.com/coorasse +[@kraflab]: https://github.com/kraflab +[@lizzyaustad]: https://github.com/lizzyaustad +[@kevintyll]: https://github.com/kevintyll +[@BookOfGreg]: https://github.com/BookOfGreg +[@arturoherrero]: https://github.com/arturoherrero +[@Eric-Guo]: https://github.com/Eric-Guo +[@ajgon]: https://github.com/ajgon +[@jpmckinney]: https://github.com/jpmckinney +[@sethcharles]: https://github.com/sethcharles +[@anilmaurya]: https://github.com/anilmaurya +[@DNNX]: https://github.com/DNNX +[@clemens]: https://github.com/clemens +[@bryanrite]: https://github.com/bryanrite +[@emmanuel]: https://github.com/emmanuel +[@thatothermitch]: https://github.com/thatothermitch +[@amw]: https://github.com/amw +[@stellard]: https://github.com/stellard +[@natemueller]: https://github.com/natemueller +[@bowsersenior]: https://github.com/bowsersenior +[@hunterae]: https://github.com/hunterae +[@nandalopes]: https://github.com/nandalopes +[@ramontayag]: https://github.com/ramontayag +[@mphalliday]: https://github.com/mphalliday +[@justinko]: https://github.com/justinko +[@EppO]: https://github.com/EppO +[@tamoya]: https://github.com/tamoya +[@mlooney]: https://github.com/mlooney +[@rrosen]: https://github.com/rrosen +[@steerio]: https://github.com/steerio +[@yuszuv]: https://github.com/yuszuv +[@albertobajo]: https://github.com/albertobajo +[@xinuc]: https://github.com/xinuc +[@oliverklee]: https://github.com/oliverklee +[@gingray]: https://github.com/gingray +[@timraymond]: https://github.com/timraymond diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc deleted file mode 100644 index 01eedda8..00000000 --- a/CHANGELOG.rdoc +++ /dev/null @@ -1,575 +0,0 @@ -Unreleased - -2.2.0 (Apr 13th, 2018) - -* Include conditions passed to authorize! in AccessDenied exception (kraflab) -* Removed support for dynamic finders (coorasse) -* Support Rails 5.2 (lizzyaustad & kevintyll) -* Use ActiveSupport standard loader (BookOfGreg) - -2.1.4 (Apr 09th, 2018) - -* Inject cancancan in ActionController::API and ActionController::Base when they are both defined (arturoherrero) - -2.1.3 (Jan 16th, 2018) - -* Fix compatibility with Rails 5 API (Eric-Guo) - -2.1.2 (Nov 22th, 2017) - -* Various bugfixes on version 2.1.0 - -2.1.0 (Nov 10th, 2017) - -* Adds support for Rails Api applications (coorasse & ajgon) -* Controller subclasses inherit skip_load_resource from superclass (jpmckinney) -* Fix instance variable not initialized warnings (sethcharles) -* Fix build_resource when model name is Action (anilmaurya) -* Smaller performance improvements (DNNX) -* Fix i18n lookup for unauthorized message (clemens) - -2.0.0 (May 18th, 2017) - -* Drop support for Rails < 4.2 (oliverklee) -* Drop support for ruby < 2.2 (coorasse) -* Drop support for InheritedResource (coorasse) -* Drop support for Sequel (coorasse) -* Drop support for Mongoid (coorasse) -* Add ability to rspec matcher to take array of abilities (gingray) -* Increase Performance (timraymond) (https://github.com/CanCanCommunity/cancancan/pull/204) -* Removed controller methods: skip_authorization, unauthorized! (coorasse) -* Removed options: nested, name, resource (coorasse) - -1.17.0 (March 26th, 2017) - -* Improve performance for the Mongoid Adapter - - -1.16.0 (February 2nd, 2017) - -* Introduce rubocop and fixes most of the issues - -1.15.0 (June 13th, 2016) - -* Add support for Rails 5 (craig1410) - -1.14.0 (May 14th, 2016) - -* Use cover for ranges -* Add support for rails 4 enum's (markpmitchell) - -1.13.1 (Oct 8th, 2015) - -* Fix #merge with empty Ability (jhawthorn) - -1.13.0 (Oct 7th, 2015) - -* Significantly improve rule lookup time (amarshall) -* Removed deprecation warnings for RSpec 3.2 (NekoNova) -* Drop support for REE and Ruby 1.x and so Rails 2 (Richard Wilson) - -1.12.0 (June 28th, 2015) - -* Add a permissions method to Ability (devaroop) - -1.11.0 (June 15th, 2015) - -* Complete cancancan#115 - Specify authorization action for parent resources. (phallguy) - -1.10.1 (January 13th, 2015) - -* Fix cancancan#168 - A bug with ActiveRecord 4.2 support causing ProtocolViolation due to named parameters not being passed in. - - -1.10.0 (January 7th, 2015) - -* Fix i18n issue for Ruby < 1.9.3 (bryanrite) - -* Fix cancancan#149 - Fix an issue loading namespaced models (darthjee) - -* Fix cancancan#160 - Support for Rails 4.2 (marshall-lee) - -* Fix cancancan#153 - More useful output in ability spec matchers (jondkinney) - - -1.9.2 (August 8th, 2014) - -* Fix cancancan#77, 78 - Fix an issue with associations for namespaced models. (jjp) - - -1.9.1 (July 21st, 2014) - -* Fix cancancan#101 - Fixes an issue where overjealous use of references would cause issues with scopes when loading associations. (bryanrite) - - -1.9.0 (July 20th, 2014) - -* Fix cancancan#59 - Parameters are automatically detected and santitized for all actions, not just create and update. (bryanrite) - -* Fix cancancan#97, 72, 40, 39, 26 - Support Active Record 4 properly with references on nested permissions. (scpike, tdg5, Crystark) - - -1.8.4 (June 24th, 2014) - -* Fix cancancan#86 - Fixes previous RSpec 3 update as there was a bug in the fix for RSpec 2.99. (bryanrite) - - -1.8.3 (June 24th, 2014) - -* Fix cancancan#85 - Remove deprecation notices for RSpec 3 and continue backwards compatibility. (andypike, bryanrite, porteta) - - -1.8.2 (June 5th, 2014) - -* Fix cancancan#75 - More specific hash-like object check. (bryanrite) - - -1.8.1 (May 27th, 2014) - -* Fix cancancan#67 - Sequel tests are run properly for JRuby. (bryanrite) - -* Fix cancancan#68 - Checks for hash-like objects in subject better. (bryanrite) - - -1.8.0 (May 8th, 2014) - -* Feature cancan#884 - Add a Sequel model adapter (szetobo) - -* Feature cancancan#3 - Permit "can?" check multiple subjects (cefigueiredo) - -* Feature cancancan#29 - Add ability to use a String that will get instance_eval'd or a Proc that will get called as the parameter method option for strong_parameter santization (svoop) - -* Feature cancancan#48 - Define a CanCanCan module. Even though it is not used, it is standard practice to define the module, and helpful for determining between CanCanCan and CanCan for external libraries. - - -1.7.1 (March 19th, 2014) - -* Fix ryanb/cancan#992 - Remove Rails 4 deprecations for scoped (thejchap & hitendrasingh) - -* Fix cancancan#16 - RSpec expectations are not explicitly required in RSpec > 2.13 (justinaiken & bryanrite) - - -1.7.0 (February 19th, 2014) - -* Feature #988 Adds support for strong_parameters (bryanrite) - -* Fix #726 - Allow multiple abilities with associations (elabs-dev) - -* Fix #864 - Fix id_param in shallow routes (francocatena) - -* Fix #871 - Fixes nested ability conditions (ricec) - -* Fix #935 - Reduce unnecessary object allocations (grosser) - -* Fix #966 - Fixes a variable name collision in nested conditions (knoopx) - -* Fix #971 - Does not execute "empty?" scope when checking class rule (matt-glover) - -* Fix #974 - Avoid unnecessary sql execution (inkstak) - - -1.6.10 (May 7, 2013) - -* fix matches_conditons_hash for string values on 1.8 (thanks rrosen) - -* work around SQL injection vulnerability in older Rails versions (thanks steerio) - issue #800 - -* add support for nested join conditions (thanks yuszuv) - issue #806 - -* fix load_resource "find_by" in mongoid resources (thanks albertobajo) - issue #705 - -* fix namespace split behavior (thanks xinuc) - issue #668 - - -1.6.9 (February 4, 2013) - -* fix inserting AND (NULL) to end of SQL queries (thanks jonsgreen) - issue #687 - -* fix merge_joins for nested association hashes (thanks DavidMikeSimon) - issues #655, #560 - -* raise error on recursive alias_action (thanks fl00r) - issue #660 - -* fix namespace controllers not loading params (thanks andhapp) - issues #670, #664 - - -1.6.8 (June 25, 2012) - -* improved support for namespaced controllers and models - -* pass :if and :unless options for load and authorize resource (thanks mauriciozaffari) - -* Travis CI badge (thanks plentz) - -* adding Ability#merge for combining multiple abilities (thanks rogercampos) - -* support for multiple MetaWhere rules (thanks andhapp) - -* various fixes for DataMapper, Mongoid, and Inherited Resource integration - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.7...1.6.8] - - -1.6.7 (October 4, 2011) - -* fixing nested resource problem caused by namespace addition - issue #482 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.6...1.6.7] - - -1.6.6 (September 28, 2011) - -* correct "return cant jump across threads" error when using check_authorization (thanks codeprimate) - issues #463, #469 - -* fixing tests in development by specifying with_model version (thanks kirkconnell) - issue #476 - -* added travis.yml file for TravisCI support (thanks bai) - issue #427 - -* better support for namespaced models (thanks whilefalse) - issues #424 - -* adding :id_param option to load_and_authorize_resource (thanks skhisma) - issue #425 - -* make default unauthorized message translatable text (thanks nhocki) - issue #409 - -* improving DataMapper behavior (thanks psanford, maxsum-corin) - issue #410, #373 - -* allow :find_by option to be full find method name - issue #335 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.5...1.6.6] - - -1.6.5 (May 18, 2011) - -* pass action and subject through AccessDenied exception when :through isn't found - issue #366 - -* many Mongoid adapter improvements (thanks rahearn, cardagin) - issues #363, #352, #343 - -* allow :through option to work with private controller methods - issue #360 - -* ensure Mongoid::Document is defined before loading Mongoid adapter - issue #359 - -* many DataMapper adapter improvements (thanks emmanuel) - issue #355 - -* handle checking nil attributes through associations (thanks thatothermitch) - issue #330 - -* improve scope merging - issue #328 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.4...1.6.5] - - -1.6.4 (March 29, 2011) - -* Fixed mongoid 'or' error - see issue #322 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.3...1.6.4] - - -1.6.3 (March 25, 2011) - -* Make sure ActiveRecord::Relation is defined before checking conditions against it so Rails 2 is supported again - see issue #312 - -* Return subject passed to authorize! - see issue #314 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.2...1.6.3] - - -1.6.2 (March 18, 2011) - -* Fixed instance loading when :singleton option is used - see issue #310 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.1...1.6.2] - - -1.6.1 (March 15, 2011) - -* Use Item.new instead of build_item for singleton resource so it doesn't effect database - see issue #304 - -* Made accessible_by action default to :index and parent action default to :show instead of :read - see issue #302 - -* Reverted Inherited Resources "collection" override since it doesn't seem to be working - see issue #305 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.0...1.6.1] - - -1.6.0 (March 11, 2011) - -* Added MetaWhere support - see issue #194 and #261 - -* Allow Active Record scopes in Ability conditions - see issue #257 - -* Added :if and :unless options to check_authorization - see issue #284 - -* Several Inherited Resources fixes (thanks aq1018, tanordheim and stefanoverna) - -* Pass action name to accessible_by call when loading a collection (thanks amw) - -* Added :prepend option to load_and_authorize_resource to load before other filters - see issue #290 - -* Fixed spacing issue in I18n message for multi-word model names - see issue #292 - -* Load resource collection for any action which doesn't have an "id" parameter - see issue #296 - -* Raise an exception when trying to make a Ability condition with both a hash of conditions and a block - see issue #269 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.5.1...1.6.0] - - -1.5.1 (January 20, 2011) - -* Fixing deeply nested conditions in Active Record adapter - see issue #246 - -* Improving Mongoid support for multiple can and cannot definitions (thanks stellard) - see issue #239 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.5.0...1.5.1] - - -1.5.0 (January 11, 2011) - -* Added an Ability generator - see issue #170 - -* Added DataMapper support (thanks natemueller) - -* Added Mongoid support (thanks bowsersenior) - -* Added skip_load_and_authorize_resource methods to controller class - see issue #164 - -* Added support for uncountable resources in index action - see issue #193 - -* Cleaned up README and added spec/README - -* Internal: renamed CanDefinition to Rule - -* Internal: added a model adapter layer for easily supporting more ORMs - -* Internal: added .rvmrc to auto-switch to 1.8.7 with gemset - see issue #231 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.4.1...1.5.0] - - -1.4.1 (November 12, 2010) - -* Renaming skip_authorization to skip_authorization_check - see issue #169 - -* Adding :through_association option to load_resource (thanks hunterae) - see issue #171 - -* The :shallow option now works with the :singleton option (thanks nandalopes) - see issue #187 - -* Play nicely with quick_scopes gem (thanks ramontayag) - see issue #183 - -* Fix odd behavior when "cache_classes = false" (thanks mphalliday) - see issue #174 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.4.0...1.4.1] - - -1.4.0 (October 5, 2010) - -* Adding Gemfile; to get specs running just +bundle+ and +rake+ - see issue #163 - -* Stop at 'cannot' definition when there are no conditions - see issue #161 - -* The :through option will now call a method with that name if instance variable doesn't exist - see issue #146 - -* Adding :shallow option to load_resource to bring back old behavior of fetching a child without a parent - -* Raise AccessDenied error when loading a child and parent resource isn't found - -* Abilities defined on a module will apply to anything that includes that module - see issue #150 and #152 - -* Abilities can be defined with a string of SQL in addition to a block so accessible_by works with a block - see issue #150 - -* Adding better support for InheritedResource - see issue #23 - -* Loading the collection instance variable (for index action) using accessible_by - see issue #137 - -* Adding action and subject variables to I18n unauthorized message - closes #142 - -* Adding check_authorization and skip_authorization controller class methods to ensure authorization is performed (thanks justinko) - see issue #135 - -* Setting initial attributes based on ability conditions in new/create actions - see issue #114 - -* Check parent attributes for nested association in index action - see issue #121 - -* Supporting nesting in can? method using hash - see issue #121 - -* Adding I18n support for Access Denied messages (thanks EppO) - see issue #103 - -* Passing no arguments to +can+ definition will pass action, class, and object to block - see issue #129 - -* Don't pass action to block in +can+ definition when using :+manage+ option - see issue #129 - -* No longer calling block in +can+ definition when checking on class - see issue #116 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.4...1.4.0] - - -1.3.4 (August 31, 2010) - -* Don't stop at +cannot+ with hash conditions when checking class (thanks tamoya) - see issue #131 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.3...1.3.4] - - -1.3.3 (August 20, 2010) - -* Switching to Rspec namespace to remove deprecation warning in Rspec 2 - see issue #119 - -* Pluralize nested associations for conditions in accessible_by (thanks mlooney) - see issue #123 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.2...1.3.3] - - -1.3.2 (August 7, 2010) - -* Fixing slice error when passing in custom resource name - see issue #112 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.1...1.3.2] - - -1.3.1 (August 6, 2010) - -* Fixing protected sanitize_sql error - see issue #111 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.0...1.3.1] - - -1.3.0 (August 6, 2010) - -* Adding :find_by option to load_resource - see issue #19 - -* Adding :singleton option to load_resource - see issue #93 - -* Supporting multiple resources in :through option for polymorphic associations - see issue #73 - -* Supporting Single Table Inheritance for "can" comparisons - see issue #55 - -* Adding :instance_name option to load/authorize_resource - see issue #44 - -* Don't pass nil to "new" to keep MongoMapper happy - see issue #63 - -* Parent resources are now authorized with :read action. - -* Changing :resource option in load/authorize_resource back to :class with ability to pass false - -* Removing :nested option in favor of :through option with separate load/authorize call - -* Moving internal logic from ResourceAuthorization to ControllerResource class - -* Supporting multiple "can" and "cannot" calls with accessible_by (thanks funny-falcon) - see issue #71 - -* Supporting deeply nested aliases - see issue #98 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.2.0...1.3.0] - - -1.2.0 (July 16, 2010) - -* Load nested parent resources on collection actions such as "index" (thanks dohzya) - -* Adding :name option to load_and_authorize_resource if it does not match controller - see issue #65 - -* Fixing issue when using accessible_by with nil can conditions (thanks jrallison) - see issue #66 - -* Pluralize table name for belongs_to associations in can conditions hash (thanks logandk) - see issue #62 - -* Support has_many association or arrays in can conditions hash - -* Adding joins clause to accessible_by when conditions are across associations - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.1.1...1.2.0] - - -1.1.1 (April 17, 2010) - -* Fixing behavior in Rails 3 by properly initializing ResourceAuthorization - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.1...1.1.1] - - -1.1.0 (April 17, 2010) - -* Supporting arrays, ranges, and nested hashes in ability conditions - -* Removing "unauthorized!" method in favor of "authorize!" in controllers - -* Adding action, subject and default_message abilities to AccessDenied exception - see issue #40 - -* Adding caching to current_ability controller method, if you're overriding this be sure to add caching too. - -* Adding "accessible_by" method to Active Record for fetching records matching a specific ability - -* Adding conditions behavior to Ability#can and fetch with Ability#conditions - see issue #53 - -* Renaming :class option to :resource for load_and_authorize_resource which now supports a symbol for non models - see issue #45 - -* Properly handle Admin::AbilitiesController in params[:controller] - see issue #46 - -* Adding be_able_to RSpec matcher (thanks dchelimsky), requires Ruby 1.8.7 or higher - see issue #54 - -* Support additional arguments to can? which get passed to the block - see issue #48 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.0.2...1.1] - - -1.0.2 (Dec 30, 2009) - -* Adding clear_aliased_actions to Ability which removes previously defined actions including defaults - see issue #20 - -* Append aliased actions (don't overwrite them) - see issue #20 - -* Adding custom message argument to unauthorized! method (thanks tjwallace) - see issue #18 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.0.1...1.0.2] - - -1.0.1 (Dec 14, 2009) - -* Adding :class option to load_resource so one can customize which class to use for the model - see issue #17 - -* Don't fetch parent of nested resource if *_id parameter is missing so it works with shallow nested routes - see issue #14 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.0.0...1.0.1] - - -1.0.0 (Dec 13, 2009) - -* Don't set resource instance variable if it has been set already - see issue #13 - -* Allowing :nested option to accept an array for deep nesting - -* Adding :nested option to load resource method - see issue #10 - -* Pass :only and :except options to before filters for load/authorize resource methods. - -* Adding :collection and :new options to load_resource method so we can specify behavior of additional actions if needed. - -* BACKWARDS INCOMPATIBLE: turning load and authorize resource methods into class methods which set up the before filter so they can accept additional arguments. - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/0.2.1...1.0.0] - - -0.2.1 (Nov 26, 2009) - -* many internal refactorings - see issues #11 and #12 - -* adding "cannot" method to define which abilities cannot be done - see issue #7 - -* support custom objects (usually symbols) in can definition - see issue #8 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/0.2.0...0.2.1] - - -0.2.0 (Nov 17, 2009) - -* fix behavior of load_and_authorize_resource for namespaced controllers - see issue #3 - -* support arrays being passed to "can" to specify multiple actions or classes - see issue #2 - -* adding "cannot?" method to ability, controller, and view which is inverse of "can?" - see issue #1 - -* BACKWARDS INCOMPATIBLE: use Ability#initialize instead of 'prepare' to set up abilities - see issue #4 - -* {see the full list of changes}[https://github.com/ryanb/cancan/compare/0.1.0...0.2.0] - - -0.1.0 (Nov 16, 2009) - -* initial release diff --git a/README.md b/README.md index 324881fe..3d43aa2d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ [Wiki](https://github.com/CanCanCommunity/cancancan/wiki) | [RDocs](http://rdoc.info/projects/CanCanCommunity/cancancan) | -[Screencast](http://railscasts.com/episodes/192-authorization-with-cancan) | +[Screencast 1](http://railscasts.com/episodes/192-authorization-with-cancan) | +[Screencast 2](https://www.youtube.com/watch?v=cTYu-OjUgDw) CanCanCan is an authorization library for Ruby >= 2.2.0 and Ruby on Rails >= 4.2 which restricts what resources a given user is allowed to access. @@ -202,7 +203,6 @@ Version 2.0 drops also support for Rails < 4.2 and ruby < 2.2 so, again, use the ## Wiki Docs -* [Upgrading to 1.6](https://github.com/CanCanCommunity/cancancan/wiki/Upgrading-to-1.6) * [Defining Abilities](https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities) * [Checking Abilities](https://github.com/CanCanCommunity/cancancan/wiki/Checking-Abilities) * [Authorizing Controller Actions](https://github.com/CanCanCommunity/cancancan/wiki/Authorizing-Controller-Actions) diff --git a/lib/cancan.rb b/lib/cancan.rb index dc06e4ad..a2ff1818 100644 --- a/lib/cancan.rb +++ b/lib/cancan.rb @@ -8,8 +8,10 @@ require 'cancan/model_adapters/abstract_adapter' require 'cancan/model_adapters/default_adapter' +require 'cancan/rules_compressor' if defined? ActiveRecord + require 'cancan/model_adapters/conditions_extractor' require 'cancan/model_adapters/active_record_adapter' require 'cancan/model_adapters/active_record_4_adapter' require 'cancan/model_adapters/active_record_5_adapter' diff --git a/lib/cancan/controller_additions.rb b/lib/cancan/controller_additions.rb index 78e813aa..547d345f 100644 --- a/lib/cancan/controller_additions.rb +++ b/lib/cancan/controller_additions.rb @@ -384,6 +384,8 @@ def cannot?(*args) end end -ActiveSupport.on_load(:action_controller) do - include CanCan::ControllerAdditions +if defined? ActiveSupport + ActiveSupport.on_load(:action_controller) do + include CanCan::ControllerAdditions + end end diff --git a/lib/cancan/exceptions.rb b/lib/cancan/exceptions.rb index d8271be6..78cdaecf 100644 --- a/lib/cancan/exceptions.rb +++ b/lib/cancan/exceptions.rb @@ -11,6 +11,9 @@ class ImplementationRemoved < Error; end # Raised when using check_authorization without calling authorized! class AuthorizationNotPerformed < Error; end + # Raised when using a wrong association name + class WrongAssociationName < Error; end + # This error is raised when a user isn't allowed to access a given controller action. # This usually happens within a call to ControllerAdditions#authorize! but can be # raised manually. diff --git a/lib/cancan/model_adapters/active_record_adapter.rb b/lib/cancan/model_adapters/active_record_adapter.rb index 75d967c7..3603ceb2 100644 --- a/lib/cancan/model_adapters/active_record_adapter.rb +++ b/lib/cancan/model_adapters/active_record_adapter.rb @@ -1,4 +1,6 @@ require_relative 'can_can/model_adapters/active_record_adapter/joins.rb' +require_relative 'conditions_extractor.rb' +require 'cancan/rules_compressor' module CanCan module ModelAdapters module ActiveRecordAdapter @@ -20,47 +22,19 @@ module ActiveRecordAdapter # query(:manage, User).conditions # => "not (self_managed = 't') AND ((manager_id = 1) OR (id = 1))" # def conditions - if @rules.size == 1 && @rules.first.base_behavior + compressed_rules = RulesCompressor.new(@rules.reverse).rules_collapsed.reverse + conditions_extractor = ConditionsExtractor.new(@model_class) + if compressed_rules.size == 1 && compressed_rules.first.base_behavior # Return the conditions directly if there's just one definition - tableized_conditions(@rules.first.conditions).dup + conditions_extractor.tableize_conditions(compressed_rules.first.conditions).dup else - extract_multiple_conditions + extract_multiple_conditions(conditions_extractor, compressed_rules) end end - def extract_multiple_conditions - @rules.reverse.inject(false_sql) do |sql, rule| - merge_conditions(sql, tableized_conditions(rule.conditions).dup, rule.base_behavior) - end - end - - def tableized_conditions(conditions, model_class = @model_class) - return conditions unless conditions.is_a? Hash - conditions.each_with_object({}) do |(name, value), result_hash| - calculate_result_hash(model_class, name, result_hash, value) - end - end - - def calculate_result_hash(model_class, name, result_hash, value) - if value.is_a? Hash - association_class = model_class.reflect_on_association(name).klass.name.constantize - nested_resulted = calculate_nested(model_class, name, result_hash, value.dup) - result_hash.merge!(tableized_conditions(nested_resulted, association_class)) - else - result_hash[name] = value - end - result_hash - end - - def calculate_nested(model_class, name, result_hash, value) - value.each_with_object({}) do |(k, v), nested| - if v.is_a? Hash - value.delete(k) - nested[k] = v - else - result_hash[model_class.reflect_on_association(name).table_name.to_sym] = value - end - nested + def extract_multiple_conditions(conditions_extractor, rules) + rules.reverse.inject(false_sql) do |sql, rule| + merge_conditions(sql, conditions_extractor.tableize_conditions(rule.conditions).dup, rule.base_behavior) end end diff --git a/lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb b/lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb index 7ef2e3b8..9667d2f3 100644 --- a/lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb +++ b/lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb @@ -6,7 +6,7 @@ module Joins # See ModelAdditions#accessible_by def joins joins_hash = {} - @rules.each do |rule| + @rules.reverse.each do |rule| merge_joins(joins_hash, rule.associations_hash) end clean_joins(joins_hash) unless joins_hash.empty? diff --git a/lib/cancan/model_adapters/conditions_extractor.rb b/lib/cancan/model_adapters/conditions_extractor.rb new file mode 100644 index 00000000..057a8d7b --- /dev/null +++ b/lib/cancan/model_adapters/conditions_extractor.rb @@ -0,0 +1,75 @@ +# this class is responsible of converting the hash of conditions +# in "where conditions" to generate the sql query +# it consists of a names_cache that helps calculating the next name given to the association +# it tries to reflect the bahavior of ActiveRecord when generating aliases for tables. +module CanCan + module ModelAdapters + class ConditionsExtractor + def initialize(model_class) + @names_cache = { model_class.table_name => [] }.with_indifferent_access + @root_model_class = model_class + end + + def tableize_conditions(conditions, model_class = @root_model_class, path_to_key = 0) + return conditions unless conditions.is_a? Hash + conditions.each_with_object({}) do |(key, value), result_hash| + if value.is_a? Hash + result_hash.merge!(calculate_result_hash(key, model_class, path_to_key, result_hash, value)) + else + result_hash[key] = value + end + result_hash + end + end + + private + + def calculate_result_hash(key, model_class, path_to_key, result_hash, value) + reflection = model_class.reflect_on_association(key) + unless reflection + raise WrongAssociationName, "association #{key} not defined in model #{model_class.name}" + end + nested_resulted = calculate_nested(model_class, result_hash, key, value.dup, path_to_key) + association_class = reflection.klass.name.constantize + tableize_conditions(nested_resulted, association_class, "#{path_to_key}_#{key}") + end + + def calculate_nested(model_class, result_hash, relation_name, value, path_to_key) + value.each_with_object({}) do |(k, v), nested| + if v.is_a? Hash + value.delete(k) + nested[k] = v + else + table_alias = generate_table_alias(model_class, relation_name, path_to_key) + result_hash[table_alias] = value + end + nested + end + end + + def generate_table_alias(model_class, relation_name, path_to_key) + table_alias = model_class.reflect_on_association(relation_name).table_name.to_sym + + if alredy_used?(table_alias, relation_name, path_to_key) + table_alias = "#{relation_name.to_s.pluralize}_#{model_class.table_name}".to_sym + + index = 1 + while alredy_used?(table_alias, relation_name, path_to_key) + table_alias = "#{table_alias}_#{index += 1}".to_sym + end + end + add_to_cache(table_alias, relation_name, path_to_key) + end + + def alredy_used?(table_alias, relation_name, path_to_key) + @names_cache[table_alias].try(:exclude?, "#{path_to_key}_#{relation_name}") + end + + def add_to_cache(table_alias, relation_name, path_to_key) + @names_cache[table_alias] ||= [] + @names_cache[table_alias] << "#{path_to_key}_#{relation_name}" + table_alias + end + end + end +end diff --git a/lib/cancan/rule.rb b/lib/cancan/rule.rb index 2578d3ba..4ab345c2 100644 --- a/lib/cancan/rule.rb +++ b/lib/cancan/rule.rb @@ -24,6 +24,18 @@ def initialize(base_behavior, action, subject, conditions, block) @block = block end + def can_rule? + base_behavior + end + + def cannot_catch_all? + !can_rule? && catch_all? + end + + def catch_all? + [nil, false, [], {}, '', ' '].include? @conditions + end + # Matches both the subject and action, not necessarily the conditions def relevant?(action, subject) subject = subject.values.first if subject.class == Hash diff --git a/lib/cancan/rules_compressor.rb b/lib/cancan/rules_compressor.rb new file mode 100644 index 00000000..15655c69 --- /dev/null +++ b/lib/cancan/rules_compressor.rb @@ -0,0 +1,20 @@ +require_relative 'conditions_matcher.rb' +module CanCan + class RulesCompressor + attr_reader :initial_rules, :rules_collapsed + + def initialize(rules) + @initial_rules = rules + @rules_collapsed = compress(@initial_rules) + end + + def compress(array) + idx = array.rindex(&:catch_all?) + return array unless idx + value = array[idx] + array[idx..-1] + .drop_while { |n| n.base_behavior == value.base_behavior } + .tap { |a| a.unshift(value) unless value.cannot_catch_all? } + end + end +end diff --git a/lib/cancan/version.rb b/lib/cancan/version.rb index 82bdbbcb..a8604adb 100644 --- a/lib/cancan/version.rb +++ b/lib/cancan/version.rb @@ -1,3 +1,3 @@ module CanCan - VERSION = '2.2.0'.freeze + VERSION = '2.3.0'.freeze end diff --git a/spec/cancan/model_adapters/active_record_adapter_spec.rb b/spec/cancan/model_adapters/active_record_adapter_spec.rb index b50f2e2e..84b7b66b 100644 --- a/spec/cancan/model_adapters/active_record_adapter_spec.rb +++ b/spec/cancan/model_adapters/active_record_adapter_spec.rb @@ -41,6 +41,7 @@ end create_table(:users) do |t| + t.string :name t.timestamps null: false end end @@ -72,6 +73,8 @@ class Comment < ActiveRecord::Base class User < ActiveRecord::Base has_many :articles + has_many :mentions + has_many :mentioned_articles, through: :mentions, source: :article end (@ability = double).extend(CanCan::Ability) @@ -249,14 +252,17 @@ class User < ActiveRecord::Base it 'returns SQL for single `can` definition in front of default `cannot` condition' do @ability.cannot :read, Article @ability.can :read, Article, published: false, secret: true - expect(@ability.model_adapter(Article, :read).conditions) - .to orderlessly_match(%("#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')) + expect(@ability.model_adapter(Article, :read)).to generate_sql(%( +SELECT "articles".* +FROM "articles" +WHERE "articles"."published" = 'f' AND "articles"."secret" = 't')) end it 'returns true condition for single `can` definition in front of default `can` condition' do @ability.can :read, Article @ability.can :read, Article, published: false, secret: true - expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'") + expect(@ability.model_adapter(Article, :read).conditions).to eq({}) + expect(@ability.model_adapter(Article, :read)).to generate_sql(%(SELECT "articles".* FROM "articles")) end it 'returns `false condition` for single `cannot` definition in front of default `cannot` condition' do @@ -279,10 +285,11 @@ class User < ActiveRecord::Base @ability.cannot :update, Article, secret: true expect(@ability.model_adapter(Article, :update).conditions) .to eq(%[not ("#{@article_table}"."secret" = 't') ] + - %[AND (("#{@article_table}"."published" = 't') ] + - %[OR ("#{@article_table}"."id" = 1))]) + %[AND (("#{@article_table}"."published" = 't') ] + + %[OR ("#{@article_table}"."id" = 1))]) expect(@ability.model_adapter(Article, :manage).conditions).to eq(id: 1) - expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'") + expect(@ability.model_adapter(Article, :read).conditions).to eq({}) + expect(@ability.model_adapter(Article, :read)).to generate_sql(%(SELECT "articles".* FROM "articles")) end it 'returns appropriate sql conditions in complex case with nested joins' do @@ -412,5 +419,51 @@ class Course < ActiveRecord::Base expect(Course.accessible_by(@ability)).to eq([valid_course]) end end + + context 'when a table references another one twice' do + before do + ActiveRecord::Schema.define do + create_table(:transactions) do |t| + t.integer :sender_id + t.integer :receiver_id + end + end + + class Transaction < ActiveRecord::Base + belongs_to :sender, class_name: 'User', foreign_key: :sender_id + belongs_to :receiver, class_name: 'User', foreign_key: :receiver_id + end + end + + it 'can filter correctly on both associations' do + sender = User.create! + receiver = User.create! + t1 = Transaction.create!(sender: sender, receiver: receiver) + t2 = Transaction.create!(sender: receiver, receiver: sender) + + ability = Ability.new(sender) + ability.can :read, Transaction, sender: { id: sender.id } + ability.can :read, Transaction, receiver: { id: sender.id } + expect(Transaction.accessible_by(ability)).to eq([t1, t2]) + end + end + + context 'when a table is references multiple times' do + it 'can filter correctly on the different associations' do + u1 = User.create!(name: 'pippo') + u2 = User.create!(name: 'paperino') + + a1 = Article.create!(user: u1) + a2 = Article.create!(user: u2) + + ability = Ability.new(u1) + ability.can :read, Article, user: { id: u1.id } + ability.can :read, Article, mentioned_users: { name: u1.name } + ability.can :read, Article, mentioned_users: { mentioned_articles: { id: a2.id } } + ability.can :read, Article, mentioned_users: { articles: { user: { name: 'deep' } } } + ability.can :read, Article, mentioned_users: { articles: { mentioned_users: { name: 'd2' } } } + expect(Article.accessible_by(ability)).to eq([a1]) + end + end end end diff --git a/spec/cancan/model_adapters/conditions_extractor_spec.rb b/spec/cancan/model_adapters/conditions_extractor_spec.rb new file mode 100644 index 00000000..a9caf241 --- /dev/null +++ b/spec/cancan/model_adapters/conditions_extractor_spec.rb @@ -0,0 +1,149 @@ +require 'spec_helper' + +if defined? CanCan::ModelAdapters::ConditionsExtractor + RSpec.describe CanCan::ModelAdapters::ConditionsExtractor do + before do + ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') + ActiveRecord::Migration.verbose = false + ActiveRecord::Schema.define do + create_table(:categories) do |t| + t.string :name + t.boolean :visible + t.timestamps null: false + end + + create_table(:projects) do |t| + t.string :name + t.timestamps null: false + end + + create_table(:articles) do |t| + t.string :name + t.timestamps null: false + t.boolean :published + t.boolean :secret + t.integer :priority + t.integer :category_id + t.integer :user_id + end + + create_table(:comments) do |t| + t.boolean :spam + t.integer :article_id + t.timestamps null: false + end + + create_table(:legacy_mentions) do |t| + t.integer :user_id + t.integer :article_id + t.timestamps null: false + end + + create_table(:users) do |t| + t.timestamps null: false + end + + create_table(:transactions) do |t| + t.integer :sender_id + t.integer :receiver_id + t.integer :supervisor_id + end + end + + class Project < ActiveRecord::Base + end + + class Category < ActiveRecord::Base + has_many :articles + end + + class Article < ActiveRecord::Base + belongs_to :category + has_many :comments + has_many :mentions + has_many :mentioned_users, through: :mentions, source: :user + belongs_to :user + end + + class Mention < ActiveRecord::Base + self.table_name = 'legacy_mentions' + belongs_to :user + belongs_to :article + end + + class Comment < ActiveRecord::Base + belongs_to :article + end + + class User < ActiveRecord::Base + has_many :articles + has_many :mentions + has_many :mentioned_articles, through: :mentions, source: :article + end + + class Transaction < ActiveRecord::Base + belongs_to :sender, class_name: 'User', foreign_key: :sender_id + belongs_to :receiver, class_name: 'User', foreign_key: :receiver_id + belongs_to :supervisor, class_name: 'User', foreign_key: :supervisor_id + end + end + + describe 'converts hash of conditions into database sql where format' do + it 'converts a simple association' do + conditions = described_class.new(User).tableize_conditions(articles: { id: 1 }) + expect(conditions).to eq(articles: { id: 1 }) + end + + it 'converts a nested association' do + conditions = described_class.new(User).tableize_conditions(articles: { category: { id: 1 } }) + expect(conditions).to eq(categories: { id: 1 }) + end + + it 'converts two associations' do + conditions = described_class.new(User).tableize_conditions(articles: { id: 2, category: { id: 1 } }) + expect(conditions).to eq(articles: { id: 2 }, categories: { id: 1 }) + end + + it 'converts has_many through' do + conditions = described_class.new(Article).tableize_conditions(mentioned_users: { id: 1 }) + expect(conditions).to eq(users: { id: 1 }) + end + + it 'converts associations named differently from the table' do + conditions = described_class.new(Transaction).tableize_conditions(sender: { id: 1 }) + expect(conditions).to eq(users: { id: 1 }) + end + + it 'converts associations properly when the same table is referenced twice' do + conditions = described_class.new(Transaction).tableize_conditions(sender: { id: 1 }, receiver: { id: 2 }) + expect(conditions).to eq(users: { id: 1 }, receivers_transactions: { id: 2 }) + end + + it 'converts very complex nested sets' do + original_conditions = { user: { id: 1 }, + mentioned_users: { name: 'a name', + mentioned_articles: { id: 2 }, + articles: { user: { name: 'deep' }, + mentioned_users: { name: 'd2' } } } } + + conditions = described_class.new(Article).tableize_conditions(original_conditions) + expect(conditions).to eq(users: { id: 1 }, + mentioned_articles_users: { id: 2 }, + mentioned_users_articles: { name: 'a name' }, + users_articles: { name: 'deep' }, + mentioned_users_articles_2: { name: 'd2' }) + end + + it 'converts complex nested sets with duplicates' do + original_conditions = { sender: { id: 'sender', articles: { id: 'article1' } }, + receiver: { id: 'receiver', articles: { id: 'article2' } } } + + conditions = described_class.new(Transaction).tableize_conditions(original_conditions) + expect(conditions).to eq(users: { id: 'sender' }, + articles: { id: 'article1' }, + receivers_transactions: { id: 'receiver' }, + articles_users: { id: 'article2' }) + end + end + end +end diff --git a/spec/cancan/rule_compressor_spec.rb b/spec/cancan/rule_compressor_spec.rb new file mode 100644 index 00000000..b8b9ba9c --- /dev/null +++ b/spec/cancan/rule_compressor_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +describe CanCan::RulesCompressor do + before do + class Blog + end + end + + def can(action, subject, args = nil) + CanCan::Rule.new(true, action, subject, args, nil) + end + + def cannot(action, subject, args = nil) + CanCan::Rule.new(false, action, subject, args, nil) + end + + context 'a "cannot catch_all" rule is in first position' do + let(:rules) do + [cannot(:read, Blog), + can(:read, Blog)] + end + it 'deletes it' do + expect(described_class.new(rules).rules_collapsed).to eq rules[1..-1] + end + end + + context 'a "can catch all" rule is in last position' do + let(:rules) do + [cannot(:read, Blog, id: 2), + can(:read, Blog, id: 1), + can(:read, Blog)] + end + + it 'deletes all previous rules' do + expect(described_class.new(rules).rules_collapsed).to eq [rules.last] + end + end + + context 'a "can catch_all" rule is in front of others can rules' do + let(:rules) do + [can(:read, Blog, id: 1), + can(:read, Blog), + can(:read, Blog, id: 3), + can(:read, Blog, author: { id: 3 }), + cannot(:read, Blog, private: true)] + end + + it 'deletes all previous rules and subsequent rules of the same type' do + expect(described_class.new(rules).rules_collapsed).to eq [rules[1], rules.last] + end + end + + context 'a "cannot catch_all" rule is in front of others cannot rules' do + let(:rules) do + [can(:read, Blog, id: 1), + can(:read, Blog), + can(:read, Blog, id: 3), + cannot(:read, Blog), + cannot(:read, Blog, private: true), + can(:read, Blog, id: 3)] + end + + it 'deletes all previous rules and subsequent rules of the same type' do + expect(described_class.new(rules).rules_collapsed).to eq [rules.last] + end + end + + context 'a lot of rules' do + let(:rules) do + [ + cannot(:read, Blog, id: 4), + can(:read, Blog, id: 1), + can(:read, Blog), + can(:read, Blog, id: 3), + cannot(:read, Blog), + cannot(:read, Blog, private: true), + can(:read, Blog, id: 3), + can(:read, Blog, id: 8), + cannot(:read, Blog, id: 5) + ] + end + + it 'minimizes the rules' do + expect(described_class.new(rules).rules_collapsed).to eq rules.last(3) + end + end + + # TODO: not supported yet + xcontext 'duplicate rules' do + let(:rules) do + [can(:read, Blog, id: 4), + can(:read, Blog, id: 1), + can(:read, Blog, id: 2), + can(:read, Blog, id: 2), + can(:read, Blog, id: 3), + can(:read, Blog, id: 2)] + end + + it 'minimizes the rules, by removing duplicates' do + expect(described_class.new(rules).rules_collapsed).to eq [rules[0], rules[1], rules[2], rules[4]] + end + end + + # TODO: not supported yet + xcontext 'merges rules' do + let(:rules) do + [can(:read, Blog, id: 4), + can(:read, Blog, id: 1), + can(:read, Blog, id: 2), + can(:read, Blog, id: 2), + can(:read, Blog, id: 3), + can(:read, Blog, id: 2)] + end + + it 'minimizes the rules, by merging them' do + expect(described_class.new(rules).rules_collapsed).to eq [can(:read, Blog, id: [4, 1, 2, 3])] + end + end +end diff --git a/spec/changelog_spec.rb b/spec/changelog_spec.rb new file mode 100644 index 00000000..f7487d77 --- /dev/null +++ b/spec/changelog_spec.rb @@ -0,0 +1,99 @@ +# credits to https://github.com/rubocop-hq/rubocop for this CHANGELOG checker +RSpec.describe 'changelog' do + subject(:changelog) do + path = File.join(File.dirname(__FILE__), '..', 'CHANGELOG.md') + File.read(path) + end + + it 'has newline at end of file' do + expect(changelog.end_with?("\n")).to be true + end + + it 'has link definitions for all implicit links' do + implicit_link_names = changelog.scan(/\[([^\]]+)\]\[\]/).flatten.uniq + implicit_link_names.each do |name| + expect(changelog).to include("[#{name}]: http") + end + end + + describe 'entry' do + subject(:entries) { lines.grep(/^\*/).map(&:chomp) } + + let(:lines) { changelog.each_line } + + it 'has a whitespace between the * and the body' do + expect(entries).to all(match(/^\* \S/)) + end + + context 'after version 1.17.0' do + let(:lines) do + changelog.each_line.take_while do |line| + !line.start_with?('## 1.17.0') + end + end + + it 'has a link to the contributors at the end' do + expect(entries).to all(match(/\(\[@\S+\]\[\](?:, \[@\S+\]\[\])*\)$/)) + end + end + + describe 'link to related issue' do + let(:issues) do + entries.map do |entry| + entry.match(/\[(?[#\d]+)\]\((?[^\)]+)\)/) + end.compact + end + + it 'has an issue number prefixed with #' do + issues.each do |issue| + expect(issue[:number]).to match(/^#\d+$/) + end + end + + it 'has a valid URL' do + issues.each do |issue| + number = issue[:number].gsub(/\D/, '') + pattern = %r{^https://github\.com/CanCanCommunity/cancancan/(?:issues|pull)/#{number}$} + expect(issue[:url]).to match(pattern) + end + end + + it 'has a colon and a whitespace at the end' do + entries_including_issue_link = entries.select do |entry| + entry.match(/^\*\s*\[/) + end + + expect(entries_including_issue_link).to all(include('): ')) + end + end + + describe 'contributor name' do + subject(:contributor_names) { lines.grep(/\A\[@/).map(&:chomp) } + + it 'has a unique contributor name' do + expect(contributor_names.uniq.size).to eq contributor_names.size + end + end + + describe 'body' do + let(:bodies) do + entries.map do |entry| + entry + .gsub(/`[^`]+`/, '``') + .sub(/^\*\s*(?:\[.+?\):\s*)?/, '') + .sub(/\s*\([^\)]+\)$/, '') + end + end + + it 'does not start with a lower case' do + bodies.each do |body| + expect(body).not_to match(/^[a-z]/) + end + end + + it 'ends with a punctuation' do + expect(bodies).to all(match(/[\.\!]$/)) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3cd49dce..26b059de 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -18,10 +18,22 @@ RSpec.configure do |config| config.filter_run focus: true config.run_all_when_everything_filtered = true + config.disable_monkey_patching = true config.mock_with :rspec config.order = 'random' config.expect_with :rspec do |c| c.syntax = :expect end + + config.include SQLHelpers +end + +RSpec::Matchers.define :generate_sql do |expected| + match do |actual| + normalized_sql(actual) == expected.gsub(/\s+/, ' ').strip + end + failure_message do |actual| + "Returned sql:\n#{normalized_sql(actual)}\ninstead of:\n#{expected.gsub(/\s+/, ' ').strip}" + end end diff --git a/spec/support/sql_helpers.rb b/spec/support/sql_helpers.rb new file mode 100644 index 00000000..d74017b5 --- /dev/null +++ b/spec/support/sql_helpers.rb @@ -0,0 +1,5 @@ +module SQLHelpers + def normalized_sql(adapter) + adapter.database_records.to_sql.strip.squeeze(' ') + end +end