From e24c36ed33c69698ffeec2828dd161ae6d0caaf6 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Sun, 26 Mar 2017 12:16:58 -0230 Subject: [PATCH 01/24] The start of Pundit code (moving away from CanCanCan) --- .../guts/controller_permission_concern.rb | 13 --- .../guts/application_controller.rb | 21 ++-- app/controllers/guts/categories_controller.rb | 3 - app/controllers/guts/contents_controller.rb | 3 - app/controllers/guts/groups_controller.rb | 3 - app/controllers/guts/index_controller.rb | 3 - app/controllers/guts/media_controller.rb | 3 - app/controllers/guts/metafields_controller.rb | 3 - .../guts/navigation_items_controller.rb | 3 - .../guts/navigations_controller.rb | 3 - app/controllers/guts/options_controller.rb | 3 - .../guts/permissions_controller.rb | 100 ------------------ app/controllers/guts/sites_controller.rb | 3 - app/controllers/guts/types_controller.rb | 3 - app/controllers/guts/users_controller.rb | 3 - app/models/guts/ability.rb | 57 ---------- app/models/guts/authorization.rb | 25 ----- app/models/guts/permission.rb | 1 - app/models/guts/user.rb | 9 -- app/policies/guts/application_policy.rb | 55 ++++++++++ ...ate_guts_permissions_and_authorizations.rb | 17 +++ guts.gemspec | 2 +- lib/guts.rb | 2 +- lib/tasks/guts_db.rake | 81 +------------- lib/tasks/guts_users.rake | 6 +- test/dummy/db/schema.rb | 29 ++--- test/fixtures/guts/authorizations.yml | 13 --- test/fixtures/guts/permissions.yml | 11 -- test/tasks/guts/db_task_test.rb | 10 +- 29 files changed, 104 insertions(+), 384 deletions(-) delete mode 100644 app/concerns/guts/controller_permission_concern.rb delete mode 100644 app/models/guts/ability.rb delete mode 100644 app/models/guts/authorization.rb create mode 100644 app/policies/guts/application_policy.rb create mode 100644 db/migrate/20170326140450_recreate_guts_permissions_and_authorizations.rb delete mode 100644 test/fixtures/guts/authorizations.yml diff --git a/app/concerns/guts/controller_permission_concern.rb b/app/concerns/guts/controller_permission_concern.rb deleted file mode 100644 index 39d12c2..0000000 --- a/app/concerns/guts/controller_permission_concern.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Guts - # Controller concern for permissionable controllers with CanCanCan - module ControllerPermissionConcern - extend ActiveSupport::Concern - - included do - # Defines the permission name for this controller - def self.permission - "Guts::#{controller_name.classify}".constantize - end - end - end -end diff --git a/app/controllers/guts/application_controller.rb b/app/controllers/guts/application_controller.rb index bc323d2..967b018 100644 --- a/app/controllers/guts/application_controller.rb +++ b/app/controllers/guts/application_controller.rb @@ -4,21 +4,12 @@ module Guts class ApplicationController < ActionController::Base include SessionConcern include MultisiteConcern + include Pundit protect_from_forgery with: :exception before_action :current_user - # Handles when user is not authorized from CanCanCan - rescue_from CanCan::AccessDenied do |exception| - # Redirects to login screen with error message - redirect_to new_session_path, alert: exception.message - end - - # Used by CanCanCan for getting the current abilities of the current user - # @return [Class] the abilities for the current user - def current_ability - @current_ability ||= Guts::Ability.new current_user - end + rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized protected @@ -27,5 +18,13 @@ def current_ability def current_user @current_user ||= User.find_by(id: session[:user_id]) end + + private + + # Sends the user to the login screen if not authorized + def user_not_authorized + flash[:alert] = 'You are not authorized.' + redirect_to new_session_path + end end end diff --git a/app/controllers/guts/categories_controller.rb b/app/controllers/guts/categories_controller.rb index 57eaf5d..1e3f377 100644 --- a/app/controllers/guts/categories_controller.rb +++ b/app/controllers/guts/categories_controller.rb @@ -3,10 +3,7 @@ module Guts # Categories controller class CategoriesController < ApplicationController - include ControllerPermissionConcern - before_action :set_category, only: [:show, :edit, :update, :destroy] - load_and_authorize_resource # Displays a list of categories def index diff --git a/app/controllers/guts/contents_controller.rb b/app/controllers/guts/contents_controller.rb index 7ac89bb..dc0d9ce 100644 --- a/app/controllers/guts/contents_controller.rb +++ b/app/controllers/guts/contents_controller.rb @@ -3,12 +3,9 @@ module Guts # Contents controller class ContentsController < ApplicationController - include ControllerPermissionConcern - before_action :set_content, only: [:show, :edit, :update, :destroy] before_action :set_type before_action :set_per_page, only: [:index] - load_and_authorize_resource # Displays a list of contents # @note This method must have a type set diff --git a/app/controllers/guts/groups_controller.rb b/app/controllers/guts/groups_controller.rb index f213076..09873c6 100644 --- a/app/controllers/guts/groups_controller.rb +++ b/app/controllers/guts/groups_controller.rb @@ -3,10 +3,7 @@ module Guts # Groups controller class GroupsController < ApplicationController - include ControllerPermissionConcern - before_action :set_group, only: [:show, :edit, :update, :destroy] - load_and_authorize_resource # Displays a list of groups def index diff --git a/app/controllers/guts/index_controller.rb b/app/controllers/guts/index_controller.rb index 7839887..57432f4 100644 --- a/app/controllers/guts/index_controller.rb +++ b/app/controllers/guts/index_controller.rb @@ -3,9 +3,6 @@ module Guts # Index controller class IndexController < ApplicationController - # Tell CanCanCan we don't have a model - authorize_resource class: false - # Displays the welcome page def index end diff --git a/app/controllers/guts/media_controller.rb b/app/controllers/guts/media_controller.rb index 9e3f1f1..668875f 100644 --- a/app/controllers/guts/media_controller.rb +++ b/app/controllers/guts/media_controller.rb @@ -3,12 +3,9 @@ module Guts # Media controller class MediaController < ApplicationController - include ControllerPermissionConcern - before_action :set_object before_action :set_medium, only: [:show, :edit, :update, :destroy, :editor_insert] before_action :set_per_page, only: [:index] - load_and_authorize_resource # Displays a list of media # @note Depending on the object passed (polymorphic) diff --git a/app/controllers/guts/metafields_controller.rb b/app/controllers/guts/metafields_controller.rb index 8ac30a1..f1e3607 100644 --- a/app/controllers/guts/metafields_controller.rb +++ b/app/controllers/guts/metafields_controller.rb @@ -3,11 +3,8 @@ module Guts # Metafields controller class MetafieldsController < ApplicationController - include ControllerPermissionConcern - before_action :set_object before_action :set_metafield, only: [:show, :edit, :update, :destroy] - load_and_authorize_resource # Displays a list of metafields def index diff --git a/app/controllers/guts/navigation_items_controller.rb b/app/controllers/guts/navigation_items_controller.rb index df2675d..9b72595 100644 --- a/app/controllers/guts/navigation_items_controller.rb +++ b/app/controllers/guts/navigation_items_controller.rb @@ -3,12 +3,9 @@ module Guts # Navigation Items controller class NavigationItemsController < ApplicationController - include ControllerPermissionConcern - before_action :set_navigation_item, only: [:show, :edit, :update, :destroy] before_action :set_navigation before_action :set_navigatable_models, exclude: [:destroy] - load_and_authorize_resource # Displays a list of navigation items def index diff --git a/app/controllers/guts/navigations_controller.rb b/app/controllers/guts/navigations_controller.rb index b8a9328..8759e22 100644 --- a/app/controllers/guts/navigations_controller.rb +++ b/app/controllers/guts/navigations_controller.rb @@ -3,10 +3,7 @@ module Guts # Navigations ontroller class NavigationsController < ApplicationController - include ControllerPermissionConcern - before_action :set_navigation, only: [:show, :edit, :update, :destroy, :reorder] - load_and_authorize_resource # Displays a list of navigations def index diff --git a/app/controllers/guts/options_controller.rb b/app/controllers/guts/options_controller.rb index 9a0ab33..67ba94c 100644 --- a/app/controllers/guts/options_controller.rb +++ b/app/controllers/guts/options_controller.rb @@ -3,11 +3,8 @@ module Guts # Options controller class OptionsController < ApplicationController - include ControllerPermissionConcern - before_action :set_option, only: [:show, :edit, :update, :destroy] before_action :set_per_page, only: [:index] - load_and_authorize_resource # Display a list of options def index diff --git a/app/controllers/guts/permissions_controller.rb b/app/controllers/guts/permissions_controller.rb index 5e5f108..dc6b0ba 100644 --- a/app/controllers/guts/permissions_controller.rb +++ b/app/controllers/guts/permissions_controller.rb @@ -3,106 +3,6 @@ module Guts # Permissions controller class PermissionsController < ApplicationController - include ControllerPermissionConcern - - before_action :set_object - before_action :set_authorization, only: [:additional, :additional_create] - load_and_authorize_resource - - # Displays the permissions - def index - end - - # Assigning a permission to an object - def new - @permission = Permission.new - @authorizations = Authorization.where(subject_id: nil) - @grouped_auths = @authorizations.group_by(&:subject_class) - end - - # Creates a permission for an object - # @note Redirects to #index if successfull or re-renders #new if not - def create - ActiveRecord::Base.transaction do - # Takes the custom authorization field from the form and loops - # and merges it into ther permission_params - params[:authorization_ids].each do |id| - permission = Permission.new permission_params.merge(authorization_id: id) - permission.save! - end - end - - # Success, all done - flash[:notice] = 'Permission was successfully granted.' - redirect_to polymorphic_path([@object, :permissions]) - rescue ActiveRecord::RecordInvalid => _ - # Something did not validate - redirect_to new_polymorphic_path([@object, :permission]) - end - - # Revokes a permission - def destroy - @permission = @object.permissions.find { |p| p.id == params[:id].to_i } - @permission.destroy if @permission - - flash[:notice] = @permission ? 'Permission was revoked.' : 'Error revoking permission.' - redirect_to polymorphic_path([@object, :permissions]) - end - - # Fine-tuned permissions on an object level - def additional - @permission = Permission.new - @objects = "#{@authorization.subject_class}".constantize.all - end - - # Creates a permission for an object at a fine level - # @note Redirects to #index if successfull or re-renders #additional if not - def additional_create - # Check if authorization exists and create if it does not - authorization = Authorization.find_or_create_by( - subject_class: @authorization.subject_class, - action: @authorization.action, - subject_id: params[:subject_id] - ) do |auth| - auth.description = @authorization.action - end - - # Save the permission - @permission = Permission.new permission_params.merge(authorization_id: authorization.id) - - if @permission.save - # Success, all done - flash[:notice] = 'Permission was successfully granted.' - redirect_to polymorphic_path([@object, :permissions]) - else - # Error - redirect_to polymorphic_path([:additional, @object, :permissions]) - end - end - - private - - # Determines the polymorphic object if available - # @note This is a `before_action` callback - # @private - def set_object - permissionable_type = params[:permissionable_type] - - return nil if permissionable_type.nil? - - param_name = "#{permissionable_type.demodulize.underscore}_id" - param_object = permissionable_type.constantize - - @object = param_object.find(params[param_name]) - end - - # Grabs the authorization from query params for fine-tune permissions - # @note This is a `before_action` callback - # @private - def set_authorization - @authorization = Authorization.find params[:authorization_id] - end - # Permits permissions from forms # @private def permission_params diff --git a/app/controllers/guts/sites_controller.rb b/app/controllers/guts/sites_controller.rb index 0739d8d..c855068 100644 --- a/app/controllers/guts/sites_controller.rb +++ b/app/controllers/guts/sites_controller.rb @@ -3,10 +3,7 @@ module Guts # Sites controller class SitesController < ApplicationController - include ControllerPermissionConcern - before_action :set_site, only: [:show, :set_default, :remove_default, :edit, :update, :destroy] - load_and_authorize_resource # Displays a list of sites def index diff --git a/app/controllers/guts/types_controller.rb b/app/controllers/guts/types_controller.rb index c5899c4..fcc3d3b 100644 --- a/app/controllers/guts/types_controller.rb +++ b/app/controllers/guts/types_controller.rb @@ -3,10 +3,7 @@ module Guts # Types controller class TypesController < ApplicationController - include ControllerPermissionConcern - before_action :set_type, only: [:show, :edit, :update, :destroy] - load_and_authorize_resource # Display a list of types def index diff --git a/app/controllers/guts/users_controller.rb b/app/controllers/guts/users_controller.rb index d28a508..898344c 100644 --- a/app/controllers/guts/users_controller.rb +++ b/app/controllers/guts/users_controller.rb @@ -3,9 +3,6 @@ module Guts # Users controller class UsersController < ApplicationController - include ControllerPermissionConcern - - load_and_authorize_resource before_action :set_user, only: [:show, :edit, :update, :destroy] # Displays a list of users diff --git a/app/models/guts/ability.rb b/app/models/guts/ability.rb deleted file mode 100644 index 04ae2fb..0000000 --- a/app/models/guts/ability.rb +++ /dev/null @@ -1,57 +0,0 @@ -module Guts - # Ability class for CanCanCan - class Ability - include CanCan::Ability - - # Initializes the user's abilities - # @param [Object|Nil] user the user object - def initialize(user) - # Call standard abilities... easier to override with decorator - standard_abilities user - end - - # Standard ability handler (makes it easier to override) - # @param [Object|Nil] user the user object - def standard_abilities(user) - user ||= Guts::User.new - - # Allow anyone logged in to see the index page - can(:read, :index) unless user.new_record? - - # Loop over group permissions first - user.groups.each do |group| - group.permissions.each do |permission| - conditions = { id: permission.authorization.subject_id } unless permission.authorization.subject_id.nil? - can( - permission.authorization.action.to_sym, - subject_class(permission.authorization.subject_class), - conditions - ) - end - end - - # Next, loop over user permissions which can override group permissions - user.permissions.each do |permission| - conditions = { id: permission.authorization.subject_id } unless permission.authorization.subject_id.nil? - can( - permission.authorization.action.to_sym, - subject_class(permission.authorization.subject_class), - conditions - ) - end - end - - private - - # Converts authorization subject into a class or symbol - # @param [String] klass the string to check - # @return [Class|Symbol] the result of the check - def subject_class(klass) - if klass.include? 'Guts::' - klass.constantize - else - klass.to_sym - end - end - end -end diff --git a/app/models/guts/authorization.rb b/app/models/guts/authorization.rb deleted file mode 100644 index 31e9bb6..0000000 --- a/app/models/guts/authorization.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Guts - # Authorizations model - class Authorization < ActiveRecord::Base - has_many :permissions - - validates :subject_class, presence: true - validates :action, presence: true - validates :subject_id, numericality: { only_integer: true }, allow_nil: true - - # Shows a class with an action - # @returns [String] compiled string with class, action, and ID (if present) - def class_with_action - string = "#{self[:subject_class]} (#{self[:action]})" - string << " for ID: #{self[:subject_id]}" if self[:subject_id] - - string - end - - # Shows a cleaner class with an action - # @returns [String] compiled string with class, action, and ID (if present) - def class_with_action_cleaner - class_with_action.gsub(/Guts::/, '').titleize - end - end -end diff --git a/app/models/guts/permission.rb b/app/models/guts/permission.rb index ecfea38..5647c14 100644 --- a/app/models/guts/permission.rb +++ b/app/models/guts/permission.rb @@ -2,6 +2,5 @@ module Guts # Polymorphic permissions model class Permission < ActiveRecord::Base belongs_to :permissionable, polymorphic: true, required: true - belongs_to :authorization, required: true end end diff --git a/app/models/guts/user.rb b/app/models/guts/user.rb index 32db2cd..0ab12a8 100644 --- a/app/models/guts/user.rb +++ b/app/models/guts/user.rb @@ -21,8 +21,6 @@ class User < ActiveRecord::Base has_many :contents has_many :permissions, as: :permissionable, dependent: :destroy - delegate :can?, :cannot?, to: :ability - scope :in_group, ->(group) { includes(:groups).where(guts_groups: { id: group.id }) } alias_attribute :title, :name @@ -33,12 +31,5 @@ class User < ActiveRecord::Base def email=(email) self[:email] = email.downcase.strip end - - # Gets the user's abilties - # @see Guts::Ability - # @return [Class] the abilities for this user - def ability - @ability ||= Guts::Ability.new self - end end end diff --git a/app/policies/guts/application_policy.rb b/app/policies/guts/application_policy.rb new file mode 100644 index 0000000..c52364d --- /dev/null +++ b/app/policies/guts/application_policy.rb @@ -0,0 +1,55 @@ +module Guts + class ApplicationPolicy + attr_reader :user, :record + + def initialize(user, record) + @user = user + @record = record + end + + def index? + false + end + + def show? + scope.where(id: record.id).exists? + end + + def create? + false + end + + def new? + create? + end + + def update? + false + end + + def edit? + update? + end + + def destroy? + false + end + + def scope + Pundit.policy_scope!(user, record.class) + end + + class Scope + attr_reader :user, :scope + + def initialize(user, scope) + @user = user + @scope = scope + end + + def resolve + scope + end + end + end +end diff --git a/db/migrate/20170326140450_recreate_guts_permissions_and_authorizations.rb b/db/migrate/20170326140450_recreate_guts_permissions_and_authorizations.rb new file mode 100644 index 0000000..7348f06 --- /dev/null +++ b/db/migrate/20170326140450_recreate_guts_permissions_and_authorizations.rb @@ -0,0 +1,17 @@ +class RecreateGutsPermissionsAndAuthorizations < ActiveRecord::Migration[5.0] + def change + drop_table :guts_permissions + drop_table :guts_authorizations + + create_table :guts_permissions do |t| + t.string :resource + t.string :grant + t.references( + :permissionable, + polymorphic: true, + index: { name: 'index_permissions_on_permissionable_type_and_permissionable' } + ) + t.timestamps null: false + end + end +end diff --git a/guts.gemspec b/guts.gemspec index 3e213f8..32525b4 100644 --- a/guts.gemspec +++ b/guts.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.add_dependency 'bcrypt', '~> 3.1' s.add_dependency 'will_paginate', '~> 3.1' s.add_dependency 'tinymce-rails', '~> 4.3' - s.add_dependency 'cancancan', '~> 1.10' + s.add_dependency 'pundit', '~> 1.1' s.add_development_dependency 'sqlite3', '~> 1.3' s.add_development_dependency 'webmock', '~> 1.22' s.add_development_dependency 'simplecov', '~> 0.12' diff --git a/lib/guts.rb b/lib/guts.rb index 5efcad5..c584b63 100644 --- a/lib/guts.rb +++ b/lib/guts.rb @@ -2,7 +2,7 @@ require 'paperclip' require 'will_paginate' require 'tinymce-rails' -require 'cancan' +require 'pundit' require 'guts/version' require 'guts/engine' require 'guts/configuration' diff --git a/lib/tasks/guts_db.rake b/lib/tasks/guts_db.rake index 7780ed0..1eff17b 100644 --- a/lib/tasks/guts_db.rake +++ b/lib/tasks/guts_db.rake @@ -25,87 +25,8 @@ namespace :guts do group.slug = 'admins' group.save! - Rake::Task['guts:db:seed:authorizations'].invoke - puts '[Guts] Database seeded' end - - desc 'Seed authorization rules based on all current controllers' - task authorizations: :environment do - load_controllers - setup_authorizations_for_controllers - - puts '[Guts] Authorizations seeded' - end - end - end -end - -def load_controllers - controllers = Dir.new("#{Guts::Engine.root}/app/controllers/guts").entries - - controllers.select do |controller| - controller =~ /_controller/ - end.each do |controller| - foo_bar = "Guts::#{controller.camelize.gsub('.rb', '')}".constantize.new - end -end - -def bad_action?(action) - bad_actions = %w(_callback with_current_site current_site) - bad_actions.any? { |name| action.include? name } -end - -def setup_authorizations_for_controllers - write_permission 'all', 'manage', 'Everything', 'All operations' - - Guts::ApplicationController.subclasses.each do |controller| - next unless controller.respond_to?(:permission) - - clazz, description = controller.permission - write_permission clazz, 'manage', description, 'All operations' - - controller.action_methods.each do |action| - next if bad_action?(action) - - action_desc, cancan_action = eval_cancan_action(action) - write_permission clazz, cancan_action, description, action_desc end end -end - -def eval_cancan_action(action) - case action.to_s - when 'index', 'show', 'search' - cancan_action = 'read' - action_desc = 'read' - when 'create', 'new' - cancan_action = 'create' - action_desc = 'create' - when 'edit', 'update' - cancan_action = 'update' - action_desc = 'edit' - when 'delete', 'destroy' - cancan_action = 'delete' - action_desc = 'delete' - else - cancan_action = action.to_s - action_desc = 'Other: ' << cancan_action - end - - [action_desc, cancan_action] -end - -def write_permission(class_name, cancan_action, title, description) - permission = Guts::Authorization.where('subject_class = ? and action = ?', class_name, cancan_action).first - - if permission.nil? - permission = Guts::Authorization.new - permission.subject_class = class_name - permission.action = cancan_action - end - - permission.title = title - permission.description = description - permission.save -end +end \ No newline at end of file diff --git a/lib/tasks/guts_users.rake b/lib/tasks/guts_users.rake index 257d01d..a3aa1c1 100644 --- a/lib/tasks/guts_users.rake +++ b/lib/tasks/guts_users.rake @@ -55,10 +55,10 @@ namespace :guts do user = Guts::User.find_by(email: args[:email]) raise StandardError, '[Guts] User not found' unless user - authorization = Guts::Authorization.find_by(subject_class: 'all') - raise StandardError, '[Guts] Authorization entry for "all" not found, have you ran authorization seeding?' unless authorization + admin_group = Guts::Group.find_by(slug: 'admins') + raise StandardError, '[Guts] Missing "Admins" group' unless authorization - user.permissions.build(authorization: authorization) + user.groups << admin_group user.save! puts '[Guts] User is now authorized for everything' diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 986d02d..d47b4d9 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161201021539) do +ActiveRecord::Schema.define(version: 20170326140450) do create_table "friendly_id_slugs", force: :cascade do |t| t.string "slug", null: false @@ -24,16 +24,6 @@ t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type" end - create_table "guts_authorizations", force: :cascade do |t| - t.string "title" - t.string "subject_class" - t.integer "subject_id" - t.string "action" - t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - create_table "guts_categories", force: :cascade do |t| t.string "title" t.string "slug" @@ -139,12 +129,13 @@ end create_table "guts_permissions", force: :cascade do |t| - t.integer "permissionable_id" + t.string "resource" + t.string "grant" t.string "permissionable_type" - t.integer "authorization_id" + t.integer "permissionable_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["permissionable_type", "permissionable_id"], name: "index_perm_on_permissionable_type_and_permissionable" + t.index ["permissionable_type", "permissionable_id"], name: "index_permissions_on_permissionable_type_and_permissionable" end create_table "guts_sites", force: :cascade do |t| @@ -181,4 +172,14 @@ t.datetime "updated_at", null: false end + create_table "versions", force: :cascade do |t| + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false + t.string "whodunnit" + t.text "object", limit: 1073741823 + t.datetime "created_at" + t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id" + end + end diff --git a/test/fixtures/guts/authorizations.yml b/test/fixtures/guts/authorizations.yml deleted file mode 100644 index b3b9cf6..0000000 --- a/test/fixtures/guts/authorizations.yml +++ /dev/null @@ -1,13 +0,0 @@ -master_authorization: - title: Everything - subject_class: all - subject_id: - action: manage - description: All operations - -type_authorization: - title: - subject_class: Guts::Type - subject_id: 3 - action: manage - description: All operations diff --git a/test/fixtures/guts/permissions.yml b/test/fixtures/guts/permissions.yml index d35137f..e69de29 100644 --- a/test/fixtures/guts/permissions.yml +++ b/test/fixtures/guts/permissions.yml @@ -1,11 +0,0 @@ -regular_user_permission: - permissionable: regular_user (Guts::User) - authorization: master_authorization - -admin_user_permission: - permissionable: admin_user (Guts::User) - authorization: master_authorization - -group_permission: - permissionable: test_group (Guts::Group) - authorization: master_authorization diff --git a/test/tasks/guts/db_task_test.rb b/test/tasks/guts/db_task_test.rb index f6678f6..1017594 100644 --- a/test/tasks/guts/db_task_test.rb +++ b/test/tasks/guts/db_task_test.rb @@ -24,19 +24,11 @@ def self.test_order end test 'test_2' do - out, = capture_io do - Rake::Task['guts:db:seed:authorizations'].invoke - end - - assert_equal '[Guts] Authorizations seeded', out.chomp - end - - test 'test_3' do out, = capture_io do Rake::Task['guts:db:seed:all'].invoke end - assert_equal "[Guts] Authorizations seeded\n[Guts] Database seeded", out.chomp + assert_equal '[Guts] Database seeded', out.chomp end end end From a4f8a35c233dfebca121f64ded90c1d0b7e163e9 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Sun, 26 Mar 2017 12:55:07 -0230 Subject: [PATCH 02/24] Partial working Pundit integration --- app/controllers/guts/index_controller.rb | 1 + app/models/guts/application_record.rb | 7 +++ app/models/guts/categorization.rb | 4 +- app/models/guts/category.rb | 3 +- app/models/guts/content.rb | 2 +- app/models/guts/group.rb | 2 +- app/models/guts/medium.rb | 2 +- app/models/guts/metafield.rb | 2 +- app/models/guts/navigation.rb | 2 +- app/models/guts/navigation_item.rb | 2 +- app/models/guts/option.rb | 2 +- app/models/guts/permission.rb | 2 +- app/models/guts/site.rb | 2 +- app/models/guts/type.rb | 2 +- app/models/guts/user.rb | 2 +- app/models/guts/user_group.rb | 2 +- app/policies/guts/index_policy.rb | 10 +++ app/views/guts/partials/_sidebar.html.erb | 74 +++++++++-------------- 18 files changed, 60 insertions(+), 63 deletions(-) create mode 100644 app/models/guts/application_record.rb create mode 100644 app/policies/guts/index_policy.rb diff --git a/app/controllers/guts/index_controller.rb b/app/controllers/guts/index_controller.rb index 57432f4..656945f 100644 --- a/app/controllers/guts/index_controller.rb +++ b/app/controllers/guts/index_controller.rb @@ -5,6 +5,7 @@ module Guts class IndexController < ApplicationController # Displays the welcome page def index + authorize [:guts, :index], :index? end end end diff --git a/app/models/guts/application_record.rb b/app/models/guts/application_record.rb new file mode 100644 index 0000000..903eb50 --- /dev/null +++ b/app/models/guts/application_record.rb @@ -0,0 +1,7 @@ +module Guts + # Base ActiveRecord class for guts + # @abstract + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + end +end \ No newline at end of file diff --git a/app/models/guts/categorization.rb b/app/models/guts/categorization.rb index 8c61dc9..963e942 100644 --- a/app/models/guts/categorization.rb +++ b/app/models/guts/categorization.rb @@ -1,9 +1,7 @@ module Guts # Categorization joiner which connects # Catgory model and Content model - # @see Guts::Category - # @see Guts::Content - class Categorization < ActiveRecord::Base + class Categorization < ApplicationRecord belongs_to :category belongs_to :content end diff --git a/app/models/guts/category.rb b/app/models/guts/category.rb index c7fd042..1306bb0 100644 --- a/app/models/guts/category.rb +++ b/app/models/guts/category.rb @@ -1,6 +1,6 @@ module Guts # Category model - class Category < ActiveRecord::Base + class Category < ApplicationRecord extend FriendlyId include NavigatableConcern include MultisiteScopeConcern @@ -9,7 +9,6 @@ class Category < ActiveRecord::Base belongs_to :site has_many :categorizations - has_many :tracks, as: :object has_many :contents, through: :categorizations has_many :media, as: :filable, dependent: :destroy has_many :metafields, as: :fieldable, dependent: :destroy diff --git a/app/models/guts/content.rb b/app/models/guts/content.rb index ddf397d..01a6176 100644 --- a/app/models/guts/content.rb +++ b/app/models/guts/content.rb @@ -1,6 +1,6 @@ module Guts # Content model - class Content < ActiveRecord::Base + class Content < ApplicationRecord extend FriendlyId include NavigatableConcern include MultisiteScopeConcern diff --git a/app/models/guts/group.rb b/app/models/guts/group.rb index 7de4862..d39984f 100644 --- a/app/models/guts/group.rb +++ b/app/models/guts/group.rb @@ -1,6 +1,6 @@ module Guts # Group model - class Group < ActiveRecord::Base + class Group < ApplicationRecord extend FriendlyId validates :title, presence: true, length: { minimum: 3 } diff --git a/app/models/guts/medium.rb b/app/models/guts/medium.rb index 2f02051..cdfda04 100644 --- a/app/models/guts/medium.rb +++ b/app/models/guts/medium.rb @@ -1,6 +1,6 @@ module Guts # Medium model which utilizes PaperClip - class Medium < ActiveRecord::Base + class Medium < ApplicationRecord include MultisiteScopeConcern # Regex used for sizing_only_images diff --git a/app/models/guts/metafield.rb b/app/models/guts/metafield.rb index eeac128..bae1598 100644 --- a/app/models/guts/metafield.rb +++ b/app/models/guts/metafield.rb @@ -1,6 +1,6 @@ module Guts # Metafield model - class Metafield < ActiveRecord::Base + class Metafield < ApplicationRecord include MultisiteScopeConcern belongs_to :site diff --git a/app/models/guts/navigation.rb b/app/models/guts/navigation.rb index 0b79d7b..6cb498b 100644 --- a/app/models/guts/navigation.rb +++ b/app/models/guts/navigation.rb @@ -1,6 +1,6 @@ module Guts # Navigation model - class Navigation < ActiveRecord::Base + class Navigation < ApplicationRecord extend FriendlyId include MultisiteScopeConcern diff --git a/app/models/guts/navigation_item.rb b/app/models/guts/navigation_item.rb index a584410..bddfebb 100644 --- a/app/models/guts/navigation_item.rb +++ b/app/models/guts/navigation_item.rb @@ -1,6 +1,6 @@ module Guts # Navigation item model - class NavigationItem < ActiveRecord::Base + class NavigationItem < ApplicationRecord include MultisiteScopeConcern before_create :set_position diff --git a/app/models/guts/option.rb b/app/models/guts/option.rb index 4371854..2b63512 100644 --- a/app/models/guts/option.rb +++ b/app/models/guts/option.rb @@ -1,6 +1,6 @@ module Guts # Option model - class Option < ActiveRecord::Base + class Option < ApplicationRecord include MultisiteScopeConcern # Regex for replacing key values with diff --git a/app/models/guts/permission.rb b/app/models/guts/permission.rb index 5647c14..04f4e66 100644 --- a/app/models/guts/permission.rb +++ b/app/models/guts/permission.rb @@ -1,6 +1,6 @@ module Guts # Polymorphic permissions model - class Permission < ActiveRecord::Base + class Permission < ApplicationRecord belongs_to :permissionable, polymorphic: true, required: true end end diff --git a/app/models/guts/site.rb b/app/models/guts/site.rb index 59d8bb8..6268582 100644 --- a/app/models/guts/site.rb +++ b/app/models/guts/site.rb @@ -1,6 +1,6 @@ module Guts # Site model - class Site < ActiveRecord::Base + class Site < ApplicationRecord validates :name, presence: true validates :domain, presence: true diff --git a/app/models/guts/type.rb b/app/models/guts/type.rb index 38da672..cde9d62 100644 --- a/app/models/guts/type.rb +++ b/app/models/guts/type.rb @@ -1,6 +1,6 @@ module Guts # Type model - class Type < ActiveRecord::Base + class Type < ApplicationRecord extend FriendlyId include NavigatableConcern include MultisiteScopeConcern diff --git a/app/models/guts/user.rb b/app/models/guts/user.rb index 0ab12a8..656656f 100644 --- a/app/models/guts/user.rb +++ b/app/models/guts/user.rb @@ -1,6 +1,6 @@ module Guts # User model - class User < ActiveRecord::Base + class User < ApplicationRecord # Regex to test email against for validation VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i diff --git a/app/models/guts/user_group.rb b/app/models/guts/user_group.rb index 99040e1..482ecd8 100644 --- a/app/models/guts/user_group.rb +++ b/app/models/guts/user_group.rb @@ -2,7 +2,7 @@ module Guts # User Group model joins User model and Group model # @see Guts::User # @see Guts::Group - class UserGroup < ActiveRecord::Base + class UserGroup < ApplicationRecord belongs_to :user belongs_to :group has_many :permissions, as: :permissionable, dependent: :destroy diff --git a/app/policies/guts/index_policy.rb b/app/policies/guts/index_policy.rb new file mode 100644 index 0000000..b1a209d --- /dev/null +++ b/app/policies/guts/index_policy.rb @@ -0,0 +1,10 @@ +module Guts + # Index policy (headless) + class IndexPolicy < Struct.new(:user, :index) + # Policy for index method + # @return [Boolean] allowed or denied + def index? + !user.nil? + end + end +end \ No newline at end of file diff --git a/app/views/guts/partials/_sidebar.html.erb b/app/views/guts/partials/_sidebar.html.erb index adc4a34..c763d7d 100644 --- a/app/views/guts/partials/_sidebar.html.erb +++ b/app/views/guts/partials/_sidebar.html.erb @@ -2,67 +2,49 @@ <% if logged_in? %> <% end %> diff --git a/app/views/guts/permissions/_permission_row.html.erb b/app/views/guts/permissions/_permission_row.html.erb new file mode 100644 index 0000000..23b4da2 --- /dev/null +++ b/app/views/guts/permissions/_permission_row.html.erb @@ -0,0 +1,14 @@ + + + <%= permission_row.resource.humanize.titleize %> + + + <%= permission_row.grant.humanize.titleize %> + + + <%= permission_row.created_at.strftime("%b #{permission_row.created_at.day.ordinalize}, %Y") %> + + + <%= link_to_destroy('Revoke', polymorphic_path([@object, permission_row])) if policy([:guts, :permission]).destroy? %> + + \ No newline at end of file diff --git a/app/views/guts/permissions/index.html.erb b/app/views/guts/permissions/index.html.erb index 06d9f10..b63e9ff 100644 --- a/app/views/guts/permissions/index.html.erb +++ b/app/views/guts/permissions/index.html.erb @@ -27,22 +27,7 @@   - <% @object.permissions.each do |permission| %> - - - <%= permission.resource.humanize.titleize %> - - - <%= permission.grant.humanize.titleize %> - - - <%= permission.created_at.strftime("%b #{permission.created_at.day.ordinalize}, %Y") %> - - - <%= link_to_destroy('Revoke', polymorphic_path([@object, permission])) if policy([:guts, :permission]).destroy? %> - - - <% end %> + <%= render partial: 'permission_row', collection: @object.permissions %> <% else %> diff --git a/app/views/guts/sites/index.html.erb b/app/views/guts/sites/index.html.erb index cdd9797..0df5b0d 100644 --- a/app/views/guts/sites/index.html.erb +++ b/app/views/guts/sites/index.html.erb @@ -20,23 +20,7 @@ <% if @sites.size > 0 %> - <% @sites.each do |site| %> - - - - - - <% end %> + <%= render partial: 'site_row', collection: @types %>
<% if site.default? %>Default: <% end %><%= site.name %><%= site.domain %>
<% else %> diff --git a/app/views/guts/sites/site_row.html.erb b/app/views/guts/sites/site_row.html.erb new file mode 100644 index 0000000..a787a54 --- /dev/null +++ b/app/views/guts/sites/site_row.html.erb @@ -0,0 +1,15 @@ + + <% if site_row.default? %>Default: <% end %><%= site_row.name %> + <%= site_row.domain %> + + <% if policy([:guts, :site]).update? %> + <% if site_row.default? %> + <%= link_to('Remove Default', remove_default_site_path(site_row)) %> + <% else %> + <%= link_to('Set as Default', set_default_site_path(site_row)) %> + <% end %> + <%= link_to('Edit', edit_site_path(site_row)) %> + <% end %> + <%= link_to_destroy('Destroy', site_row) if policy([:guts, :site]).destroy? %> + + \ No newline at end of file diff --git a/app/views/guts/types/_type_row.html.erb b/app/views/guts/types/_type_row.html.erb new file mode 100644 index 0000000..699a7cc --- /dev/null +++ b/app/views/guts/types/_type_row.html.erb @@ -0,0 +1,8 @@ + + <%= type_row.title %> + <%= pluralize type_row.contents.size, 'record', plural: 'records' %> + + <%= link_to('Edit', edit_type_path(type_row)) if policy([:guts, :type]).update? %> + <%= link_to_destroy('Destroy', type_row) if policy([:guts, :type]).destroy? %> + + \ No newline at end of file diff --git a/app/views/guts/types/index.html.erb b/app/views/guts/types/index.html.erb index 17fe965..a62b361 100644 --- a/app/views/guts/types/index.html.erb +++ b/app/views/guts/types/index.html.erb @@ -20,16 +20,7 @@ <% if @types.size > 0 %> - <% @types.each do |type| %> - - - - - - <% end %> + <%= render partial: 'type_row', collection: @types %>
<%= type.title %><%= pluralize type.contents.size, 'record', plural: 'records' %>
<% else %> diff --git a/app/views/guts/users/_user_row.html.erb b/app/views/guts/users/_user_row.html.erb new file mode 100644 index 0000000..a416fd9 --- /dev/null +++ b/app/views/guts/users/_user_row.html.erb @@ -0,0 +1,12 @@ + + <%= gravatar_for user_row %> + <%= user_row.name %> + <%= user_row.email %> + <%= user_row.groups.map(&:title).join(', ') %> + + <%= link_to 'Full Info', user_path(user_row) %> + <%= link_to('Permissions', user_permissions_path(user_row)) if policy([:guts, :permission]).index? %> + <%= link_to('Edit', edit_user_path(user_row)) if policy([:guts, :user]).update? %> + <%= link_to_destroy('Destroy', user_row) if policy([:guts, :user]).destroy? %> + + \ No newline at end of file diff --git a/app/views/guts/users/index.html.erb b/app/views/guts/users/index.html.erb index 8928c4a..811f80f 100644 --- a/app/views/guts/users/index.html.erb +++ b/app/views/guts/users/index.html.erb @@ -20,20 +20,7 @@ <% if @users.size > 0 %> - <% @users.each do |user| %> - - - - - - - - <% end %> + <%= render partial: 'user_row', collection: @users %>
<%= gravatar_for user %><%= user.name %><%= user.email %><%= user.groups.map(&:title).join(', ') %>
<% else %> From a655565879de1c42f502bbb2ab50c12a67715cf9 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 29 Mar 2017 09:09:26 -0230 Subject: [PATCH 19/24] Moved loop template bits to their own partials --- .../_navigation_item_row.html.erb | 12 ++++++++ .../guts/navigation_items/index.html.erb | 15 +--------- .../navigations/_navigation_item_row.html.erb | 8 +++++ .../guts/navigations/_navigation_row.html.erb | 18 +++++++++++ app/views/guts/navigations/index.html.erb | 30 +------------------ ...tion_row.html.erb => _option_row.html.erb} | 0 .../{site_row.html.erb => _site_row.html.erb} | 0 7 files changed, 40 insertions(+), 43 deletions(-) create mode 100644 app/views/guts/navigation_items/_navigation_item_row.html.erb create mode 100644 app/views/guts/navigations/_navigation_item_row.html.erb create mode 100644 app/views/guts/navigations/_navigation_row.html.erb rename app/views/guts/options/{option_row.html.erb => _option_row.html.erb} (100%) rename app/views/guts/sites/{site_row.html.erb => _site_row.html.erb} (100%) diff --git a/app/views/guts/navigation_items/_navigation_item_row.html.erb b/app/views/guts/navigation_items/_navigation_item_row.html.erb new file mode 100644 index 0000000..8a43a40 --- /dev/null +++ b/app/views/guts/navigation_items/_navigation_item_row.html.erb @@ -0,0 +1,12 @@ + + <%= navigation_item_row.title %> + + + to: <%= navigation_item_row.custom? ? 'Custom' : navigation_item_row.navigatable.navigatable_format %> <%= "(#{navigation_item_row.custom})" if navigation_item_row.custom? %> + + + + <%= link_to('Edit', edit_navigation_navigation_item_path(navigation_item_row.navigation, navigation_item_row)) if policy([:guts, :navigation_item]).update? %> + <%= link_to_destroy('Destroy', navigation_navigation_item_path(navigation_item_row.navigation, navigation_item_row)) if policy([:guts, :navigation_item]).destroy? %> + + \ No newline at end of file diff --git a/app/views/guts/navigation_items/index.html.erb b/app/views/guts/navigation_items/index.html.erb index fc16c43..8136cee 100644 --- a/app/views/guts/navigation_items/index.html.erb +++ b/app/views/guts/navigation_items/index.html.erb @@ -21,20 +21,7 @@ <% if @navigation_items.size > 0 %> - <% @navigation_items.each do |navigation_item| %> - - - - - - <% end %> + <%= render partial: 'navigation_item_row', collection: @navigation_items %>
<%= navigation_item.title %> - - to: <%= navigation_item.custom? ? 'Custom' : navigation_item.navigatable.navigatable_format %> <%= "(#{navigation_item.custom})" if navigation_item.custom? %> - -
<% else %> diff --git a/app/views/guts/navigations/_navigation_item_row.html.erb b/app/views/guts/navigations/_navigation_item_row.html.erb new file mode 100644 index 0000000..252f16a --- /dev/null +++ b/app/views/guts/navigations/_navigation_item_row.html.erb @@ -0,0 +1,8 @@ +
  • + + <%= navigation_item_row.title %>  —>  <%= navigation_item_row.custom? ? 'Custom' : navigation_item_row.navigatable.navigatable_format %> <%= "to: #{navigation_item_row.custom}" if navigation_item_row.custom? %> + +
  • \ No newline at end of file diff --git a/app/views/guts/navigations/_navigation_row.html.erb b/app/views/guts/navigations/_navigation_row.html.erb new file mode 100644 index 0000000..6f1fa6b --- /dev/null +++ b/app/views/guts/navigations/_navigation_row.html.erb @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/app/views/guts/navigations/index.html.erb b/app/views/guts/navigations/index.html.erb index 8a0abe2..81ea6bd 100644 --- a/app/views/guts/navigations/index.html.erb +++ b/app/views/guts/navigations/index.html.erb @@ -17,35 +17,7 @@
    <% if @navigations.size > 0 %> - <% @navigations.each do |navigation| %> - - <% end %> + <%= render partial: 'navigation_row', collection: @navigations %> <% else %>

    No navigational menus have been added.

    diff --git a/app/views/guts/options/option_row.html.erb b/app/views/guts/options/_option_row.html.erb similarity index 100% rename from app/views/guts/options/option_row.html.erb rename to app/views/guts/options/_option_row.html.erb diff --git a/app/views/guts/sites/site_row.html.erb b/app/views/guts/sites/_site_row.html.erb similarity index 100% rename from app/views/guts/sites/site_row.html.erb rename to app/views/guts/sites/_site_row.html.erb From 1ea73da41421dc3d493254f1faa38161508fcc29 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 29 Mar 2017 09:52:48 -0230 Subject: [PATCH 20/24] Created a granted concern to DRY up code --- app/concerns/guts/granted_concern.rb | 48 ++++++++++++++++++++++++ app/concerns/guts/multisite_concern.rb | 34 ++++++++--------- app/concerns/guts/navigatable_concern.rb | 24 ++++++------ app/models/guts/group.rb | 10 +---- app/models/guts/user.rb | 11 +----- app/policies/guts/application_policy.rb | 34 ++++++++--------- test/fixtures/guts/permissions.yml | 4 +- test/models/guts/group_test.rb | 17 ++++++++- test/models/guts/user_test.rb | 17 ++++++++- 9 files changed, 129 insertions(+), 70 deletions(-) create mode 100644 app/concerns/guts/granted_concern.rb diff --git a/app/concerns/guts/granted_concern.rb b/app/concerns/guts/granted_concern.rb new file mode 100644 index 0000000..7bd1a5f --- /dev/null +++ b/app/concerns/guts/granted_concern.rb @@ -0,0 +1,48 @@ +module Guts + # Model concern for permissionable models + module GrantedConcern + extend ActiveSupport::Concern + + included do + # Determines if a user has permission to a resource and type + # @param [Symbol|String|Class|Array] resource the resource to use + # @param [Symbol|String] method the method for the resource + # @return [Boolean] if user has access to resource and method + def granted?(resource, method) + grants = permissions.where(resource: grant_resource_string(resource)).pluck(:grant) + grants.include? method.to_s + end + + private + + # Generates a resource string from input + # @param [Symbol|String|Class|Array] resource the resource to use + # @return [String] the compiled resource string + def grant_resource_string(resource) + if resource.is_a?(Array) + resource.map { |bit| grant_class_name bit }.join('::') + else + grant_class_name resource + end + end + + # Determines the class name + # @param [Symbol|String|Class|Array] subject the subject to parse + # @return [String] the class name + # @note This is from Pundit's finder + def grant_class_name(subject) + if subject.respond_to?(:model_name) + subject.model_name + elsif subject.class.respond_to?(:model_name) + subject.class.model_name + elsif subject.is_a?(Class) + subject + elsif subject.is_a?(Symbol) + subject.to_s.camelize + else + subject.class + end + end + end + end +end diff --git a/app/concerns/guts/multisite_concern.rb b/app/concerns/guts/multisite_concern.rb index 3bc957b..ec68968 100644 --- a/app/concerns/guts/multisite_concern.rb +++ b/app/concerns/guts/multisite_concern.rb @@ -5,25 +5,25 @@ module MultisiteConcern included do around_action :with_current_site - end - - # Sets the current site based on the request host - # @return [Object, nil] the current site if found or nil - def current_site - @current_site ||= Site.find_by(domain: request.host) - end - # Wraps all actions to set current site for multisite - # @see Guts::MultisiteConcern#current_site - # @note This is a `around_action` method - def with_current_site - # Get the current site and begin action - Site.current_id = current_site.try(:id) + # Sets the current site based on the request host + # @return [Object, nil] the current site if found or nil + def current_site + @current_site ||= Site.find_by(domain: request.host) + end + + # Wraps all actions to set current site for multisite + # @see Guts::MultisiteConcern#current_site + # @note This is a `around_action` method + def with_current_site + # Get the current site and begin action + Site.current_id = current_site.try(:id) - yield - ensure - # Clean up the current site ID - Site.current_id = nil + yield + ensure + # Clean up the current site ID + Site.current_id = nil + end end end end diff --git a/app/concerns/guts/navigatable_concern.rb b/app/concerns/guts/navigatable_concern.rb index 48827a3..7563a9c 100644 --- a/app/concerns/guts/navigatable_concern.rb +++ b/app/concerns/guts/navigatable_concern.rb @@ -4,18 +4,20 @@ module Guts module NavigatableConcern extend ActiveSupport::Concern - # Renders a string based on options from the model - # @note This method uses instance_eval to get the instance variables.. - # :title to self.title, :type.title to self.type.title - # @return [String] compiled string based on format and variables - # @see Guts::NavigatableConcern::ClassMethods#navigatable - def navigatable_format - formatted = self.class.navigatable_opts[:format] - self.class.navigatable_opts[:variables].each do |var| - formatted = formatted.gsub(/\:#{var}/, instance_eval("self.#{var}")) + included do + # Renders a string based on options from the model + # @note This method uses instance_eval to get the instance variables.. + # :title to self.title, :type.title to self.type.title + # @return [String] compiled string based on format and variables + # @see Guts::NavigatableConcern::ClassMethods#navigatable + def navigatable_format + formatted = self.class.navigatable_opts[:format] + self.class.navigatable_opts[:variables].each do |var| + formatted = formatted.gsub(/\:#{var}/, instance_eval("self.#{var}")) + end + + formatted end - - formatted end # Class methods for the concern diff --git a/app/models/guts/group.rb b/app/models/guts/group.rb index 7083989..aefad33 100644 --- a/app/models/guts/group.rb +++ b/app/models/guts/group.rb @@ -2,6 +2,7 @@ module Guts # Group model class Group < ApplicationRecord extend FriendlyId + include GrantedConcern validates :title, presence: true, length: { minimum: 3 } @@ -18,14 +19,5 @@ class Group < ApplicationRecord def should_generate_new_friendly_id? title_changed? end - - # Determines if a user has permission to a resource and type - # @param [Symbol|String] resource the resource (controller) name - # @param [Symbol|String] method the method for the resource - # @return [Boolean] if user has access to resource and method - def grants?(resource, method) - grants = permissions.where(resource: resource.to_s).pluck(:grant) - grants.include? method.to_s.delete('?') - end end end diff --git a/app/models/guts/user.rb b/app/models/guts/user.rb index f990e5b..f500ae5 100644 --- a/app/models/guts/user.rb +++ b/app/models/guts/user.rb @@ -1,6 +1,8 @@ module Guts # User model class User < ApplicationRecord + include GrantedConcern + # Regex to test email against for validation VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i @@ -30,14 +32,5 @@ class User < ApplicationRecord def email=(email) self[:email] = email.downcase.strip end - - # Determines if a user has permission to a resource and type - # @param [Symbol|String] resource the resource (controller) name - # @param [Symbol|String] method the method for the resource - # @return [Boolean] if user has access to resource and method - def grants?(resource, method) - grants = permissions.where(resource: resource.to_s).pluck(:grant) - grants.include? method.to_s.delete('?') - end end end diff --git a/app/policies/guts/application_policy.rb b/app/policies/guts/application_policy.rb index 798ea60..0b8069f 100644 --- a/app/policies/guts/application_policy.rb +++ b/app/policies/guts/application_policy.rb @@ -22,7 +22,7 @@ def initialize(user, record) # Index method policy # @return [Boolean] allowed or denied def index? - standard_check :index? + standard_check :index end # Show method policy @@ -34,7 +34,7 @@ def show? # Create method policy # @return [Boolean] allowed or denied def create? - standard_check :create? + standard_check :create end # New method policy @@ -46,7 +46,7 @@ def new? # Update method policy # @return [Boolean] allowed or denied def update? - standard_check :update? + standard_check :update end # Edit method policy @@ -58,7 +58,7 @@ def edit? # Destroy method policy # @return [Boolean] allowed or denied def destroy? - standard_check :destory? + standard_check :destory end # Scope for policy @@ -94,41 +94,39 @@ def resolve # Checks if a user is granted access to a resource and method # @private - # @param [Symbol] resource the resource (controller) to check - # @param [Symbol] method the method for the resource + # @param [Symbol|String|Class|Array] resource the resource (controller) to check + # @param [Symbol|String] method the method for the resource # @return [Boolean] accepted or denied - def user_grants?(resource, method) - @user.grants? resource, method + def user_granted?(resource, method) + @user.granted? resource, method end # Checks if a user's groups are granted access to a resource and method # @private - # @param [Symbol] resource the resource (controller) to check - # @param [Symbol] method the method for the resource + # @param [Symbol|String|Class|Array] resource the resource (controller) to check + # @param [Symbol|String] method the method for the resource # @return [Boolean] accepted or denied - def groups_grants?(resource, method) + def groups_granted?(resource, method) @user.groups.any? do |group| - group.grants? resource, method + group.granted? resource, method end end # Checks if user is in the admin's group # @private # @return [Boolean] - def is_admin? + def admin? @user.groups.map(&:slug).include? 'admins' end # DRY code for checking methods # @private - # @param [Symbol] method the method to check + # @param [Symbol|String] method the method to check # @return [Boolean] def standard_check(method) - class_name = self.class.to_s.gsub('Policy', '').demodulize.underscore.to_sym + class_name = self.class.to_s.remove('Policy') - is_admin? || - user_grants?(class_name, method) || - groups_grants?(class_name, method) + admin? || user_granted?(class_name, method) || groups_granted?(class_name, method) end end end diff --git a/test/fixtures/guts/permissions.yml b/test/fixtures/guts/permissions.yml index c24e7b3..e7f82db 100644 --- a/test/fixtures/guts/permissions.yml +++ b/test/fixtures/guts/permissions.yml @@ -1,9 +1,9 @@ permission_one: - resource: type + resource: Guts::Type grant: index permissionable: admin_user (Guts::User) permission_two: - resource: type + resource: Guts::Type grant: destroy permissionable: admins (Guts::Group) \ No newline at end of file diff --git a/test/models/guts/group_test.rb b/test/models/guts/group_test.rb index 5b5227a..fbe3af3 100644 --- a/test/models/guts/group_test.rb +++ b/test/models/guts/group_test.rb @@ -35,11 +35,24 @@ class GroupTest < ActiveSupport::TestCase assert_operator group.metafields.size, :>, 0 end + test 'should determine grant resource string' do + group = guts_groups :admins + result = group.send :grant_resource_string, %i(guts navigation_item) + result2 = group.send :grant_resource_string, %i(guts type) + result3 = group.send :grant_resource_string, Guts::User + result4 = group.send :grant_resource_string, group + + assert_equal 'Guts::NavigationItem', result + assert_equal 'Guts::Type', result2 + assert_equal 'Guts::User', result3 + assert_equal 'Guts::Group', result4 + end + test 'should check grants' do group = guts_groups :admins - assert_equal false, group.grants?(:non_existant_resource, :non_existant_method) - assert_equal true, group.grants?(:type, :destroy) # From fixture + assert_equal false, group.granted?(:non_existant_resource, :non_existant_method) + assert_equal true, group.granted?(%i(guts type), :destroy) # From fixture end end end diff --git a/test/models/guts/user_test.rb b/test/models/guts/user_test.rb index 2de8244..36ec941 100644 --- a/test/models/guts/user_test.rb +++ b/test/models/guts/user_test.rb @@ -61,11 +61,24 @@ class UserTest < ActiveSupport::TestCase assert_operator user.metafields.size, :>, 0 end + test 'should determine grant resource string' do + user = guts_users :admin_user + result = user.send :grant_resource_string, %i(guts navigation_item) + result2 = user.send :grant_resource_string, %i(guts type) + result3 = user.send :grant_resource_string, Guts::User + result4 = user.send :grant_resource_string, user + + assert_equal 'Guts::NavigationItem', result + assert_equal 'Guts::Type', result2 + assert_equal 'Guts::User', result3 + assert_equal 'Guts::User', result4 + end + test 'should check grants' do user = guts_users :admin_user - assert_equal false, user.grants?(:non_existant_resource, :non_existant_method) - assert_equal true, user.grants?(:type, :index) # From fixture + assert_equal false, user.granted?(:non_existant_resource, :non_existant_method) + assert_equal true, user.granted?(%i(guts type), :index) # From fixture end end end From 5e97cf4bea20ed2cd61398cd0939c77c5679748f Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 29 Mar 2017 10:05:11 -0230 Subject: [PATCH 21/24] Permissions now support namespaces --- app/controllers/guts/permissions_controller.rb | 7 ++++--- app/views/guts/permissions/_permission_row.html.erb | 4 ++-- app/views/guts/permissions/new.html.erb | 2 +- lib/guts/engine.rb | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/controllers/guts/permissions_controller.rb b/app/controllers/guts/permissions_controller.rb index 9a729c0..393414c 100644 --- a/app/controllers/guts/permissions_controller.rb +++ b/app/controllers/guts/permissions_controller.rb @@ -18,7 +18,7 @@ def new # Loop over all policies @policies = {} - Dir.new("#{Guts::Engine.root}/app/policies/guts") + Dir.new(Guts::Engine.root.join('app', 'policies', 'guts')) .entries .select { |file| file =~ /_policy/ } .each do |file| @@ -26,8 +26,9 @@ def new next if file =~ /application_policy/ # Get resource name, merge grants with standard grants - resource = file.gsub('_policy.rb', '') - grants = standard_grants | grant_methods("Guts::#{file.camelize.gsub('.rb', '')}".constantize) + klass = "Guts::#{file.camelize.gsub('.rb', '')}" + resource = klass.remove 'Policy' + grants = standard_grants | grant_methods(klass.constantize) @policies[resource] = grants end diff --git a/app/views/guts/permissions/_permission_row.html.erb b/app/views/guts/permissions/_permission_row.html.erb index 23b4da2..3320e53 100644 --- a/app/views/guts/permissions/_permission_row.html.erb +++ b/app/views/guts/permissions/_permission_row.html.erb @@ -1,9 +1,9 @@ - <%= permission_row.resource.humanize.titleize %> + <%= permission_row.resource %> - <%= permission_row.grant.humanize.titleize %> + <%= permission_row.grant %> <%= permission_row.created_at.strftime("%b #{permission_row.created_at.day.ordinalize}, %Y") %> diff --git a/app/views/guts/permissions/new.html.erb b/app/views/guts/permissions/new.html.erb index 93bb25d..c13a7cf 100644 --- a/app/views/guts/permissions/new.html.erb +++ b/app/views/guts/permissions/new.html.erb @@ -29,7 +29,7 @@
    <% @policies.each do |resource, grants| %>
    - <%= resource.humanize.titleize %> + <%= resource.demodulize.titleize %> <% grants.each do |grant| %> <% has_grant = @object.permissions.find { |p| p.resource == resource && p.grant == grant } %> diff --git a/lib/guts/engine.rb b/lib/guts/engine.rb index fb3ea63..70eab07 100644 --- a/lib/guts/engine.rb +++ b/lib/guts/engine.rb @@ -11,7 +11,7 @@ class Engine < ::Rails::Engine # Allow decorator usage for extending Guts config.to_prepare do - Dir.glob("#{Rails.root}/app/decorators/*/guts/*_decorator*.rb").each do |c| + Dir.glob(Rails.root.join('app', 'decorators', '*', 'guts', '*_decorator*.rb')).each do |c| require_dependency(c) end end From a9b958ee84ff13a7b79afffb1fbbf2d5eabb3d6f Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 29 Mar 2017 10:10:09 -0230 Subject: [PATCH 22/24] Constantize class name to prevent strings --- app/policies/guts/application_policy.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/policies/guts/application_policy.rb b/app/policies/guts/application_policy.rb index 0b8069f..6e2561f 100644 --- a/app/policies/guts/application_policy.rb +++ b/app/policies/guts/application_policy.rb @@ -94,7 +94,7 @@ def resolve # Checks if a user is granted access to a resource and method # @private - # @param [Symbol|String|Class|Array] resource the resource (controller) to check + # @param [Symbol|Class|Array] resource the resource (controller) to check # @param [Symbol|String] method the method for the resource # @return [Boolean] accepted or denied def user_granted?(resource, method) @@ -103,7 +103,7 @@ def user_granted?(resource, method) # Checks if a user's groups are granted access to a resource and method # @private - # @param [Symbol|String|Class|Array] resource the resource (controller) to check + # @param [Symbol|Class|Array] resource the resource (controller) to check # @param [Symbol|String] method the method for the resource # @return [Boolean] accepted or denied def groups_granted?(resource, method) @@ -124,7 +124,7 @@ def admin? # @param [Symbol|String] method the method to check # @return [Boolean] def standard_check(method) - class_name = self.class.to_s.remove('Policy') + class_name = self.class.to_s.remove('Policy').constantize admin? || user_granted?(class_name, method) || groups_granted?(class_name, method) end From 6f06029c93d948105df0a01c4e3421b1eae8ed91 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Thu, 30 Mar 2017 18:10:31 -0230 Subject: [PATCH 23/24] Policy testing to start.... --- app/concerns/guts/granted_concern.rb | 6 ++---- lib/guts/version.rb | 2 +- test/models/guts/user_test.rb | 4 ++++ test/policies/guts/category_policy_test.rb | 10 ++++++++++ test/support/guts/login_support.rb | 3 ++- test/support/guts/policy_test_support.rb | 11 +++++++++++ test/test_helper.rb | 1 + 7 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 test/policies/guts/category_policy_test.rb create mode 100644 test/support/guts/policy_test_support.rb diff --git a/app/concerns/guts/granted_concern.rb b/app/concerns/guts/granted_concern.rb index 7bd1a5f..f6cc48f 100644 --- a/app/concerns/guts/granted_concern.rb +++ b/app/concerns/guts/granted_concern.rb @@ -33,12 +33,10 @@ def grant_resource_string(resource) def grant_class_name(subject) if subject.respond_to?(:model_name) subject.model_name - elsif subject.class.respond_to?(:model_name) - subject.class.model_name - elsif subject.is_a?(Class) - subject elsif subject.is_a?(Symbol) subject.to_s.camelize + elsif subject.is_a?(Class) + subject else subject.class end diff --git a/lib/guts/version.rb b/lib/guts/version.rb index cd12d74..d70ba08 100644 --- a/lib/guts/version.rb +++ b/lib/guts/version.rb @@ -1,5 +1,5 @@ # Guts' module namespace module Guts # Current Guts version - VERSION = '2.1.0'.freeze + VERSION = '3.0.0'.freeze end diff --git a/test/models/guts/user_test.rb b/test/models/guts/user_test.rb index 36ec941..79751bb 100644 --- a/test/models/guts/user_test.rb +++ b/test/models/guts/user_test.rb @@ -67,11 +67,15 @@ class UserTest < ActiveSupport::TestCase result2 = user.send :grant_resource_string, %i(guts type) result3 = user.send :grant_resource_string, Guts::User result4 = user.send :grant_resource_string, user + result5 = user.send :grant_resource_string, Hash + result6 = user.send :grant_resource_string, 'Boo' assert_equal 'Guts::NavigationItem', result assert_equal 'Guts::Type', result2 assert_equal 'Guts::User', result3 assert_equal 'Guts::User', result4 + assert_equal Hash, result5 + assert_equal String, result6 end test 'should check grants' do diff --git a/test/policies/guts/category_policy_test.rb b/test/policies/guts/category_policy_test.rb new file mode 100644 index 0000000..ac7a3d2 --- /dev/null +++ b/test/policies/guts/category_policy_test.rb @@ -0,0 +1,10 @@ +require 'test_helper' + +module Guts + class CategoryPolicyTest < PolicyTest + def test_new + user = guts_users :admin_user + assert permit(user, Category.new, :new) + end + end +end \ No newline at end of file diff --git a/test/support/guts/login_support.rb b/test/support/guts/login_support.rb index 4364bc5..8dc2500 100644 --- a/test/support/guts/login_support.rb +++ b/test/support/guts/login_support.rb @@ -1,5 +1,6 @@ -# Login a admin user before testing controllers +# ActionController module ActionController + # Login a admin user before testing controllers class TestCase include Guts::SessionConcern diff --git a/test/support/guts/policy_test_support.rb b/test/support/guts/policy_test_support.rb new file mode 100644 index 0000000..86b43a2 --- /dev/null +++ b/test/support/guts/policy_test_support.rb @@ -0,0 +1,11 @@ +# Allow for testing policies +# From http://stackoverflow.com/a/30754295 +class PolicyTest < ActiveSupport::TestCase + def permit(current_user, record, action) + self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?") + end + + def forbid(current_user, record, action) + !permit(current_user, record, action) + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 6c490d7..6b19915 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -5,6 +5,7 @@ SimpleCov.start 'rails' do add_group 'Concerns', '/app/concerns' add_group 'Generators', '/lib/generators' + add_group 'Policies', '/app/policies' add_filter 'lib/guts/version.rb' # No need to test version file... doesnt work. add_filter 'lib/tasks/guts_tasks.rake' # Inconsistant coverage reports, not sure why add_filter 'lib/tasks/guts_users.rake' # Inconsistant coverage reports, not sure why From 560cf223bd9ecc8717405c23cbc180f7413c7db7 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Fri, 31 Mar 2017 01:43:40 -0230 Subject: [PATCH 24/24] Policy tests and code clean up --- app/concerns/guts/granted_concern.rb | 6 ++--- app/helpers/guts/categories_helper.rb | 3 +-- app/helpers/guts/contents_helper.rb | 3 +-- app/helpers/guts/groups_helper.rb | 3 +-- app/helpers/guts/index_helper.rb | 3 +-- app/helpers/guts/media_helper.rb | 3 +-- app/helpers/guts/metafields_helper.rb | 3 +-- app/helpers/guts/navigation_items_helper.rb | 3 +-- app/helpers/guts/navigations_helper.rb | 3 +-- app/helpers/guts/options_helper.rb | 3 +-- app/helpers/guts/permissions_helper.rb | 9 +------ app/helpers/guts/sites_helper.rb | 3 +-- app/helpers/guts/types_helper.rb | 3 +-- app/helpers/guts/users_helper.rb | 4 ++- app/models/guts/permission.rb | 1 + ...lication_controller_with_multisite_test.rb | 8 +++--- test/helpers/permissions_helper_test.rb | 9 ------- test/helpers/users_helper_test.rb | 27 +++++++++++++------ test/mailers/guts/user_mailer_test.rb | 9 +++---- test/models/guts/group_test.rb | 13 --------- test/models/guts/permission_test.rb | 22 +++++++++++++++ test/models/guts/user_test.rb | 17 ------------ test/policies/guts/application_policy_test.rb | 22 +++++++++++++++ test/policies/guts/category_policy_test.rb | 8 +++--- test/policies/guts/content_policy_test.rb | 12 +++++++++ test/policies/guts/group_policy_test.rb | 12 +++++++++ test/policies/guts/index_policy_test.rb | 11 ++++++++ test/policies/guts/medium_policy_test.rb | 12 +++++++++ test/policies/guts/metafield_policy_test.rb | 12 +++++++++ test/policies/guts/navigation_item_policy.rb | 12 +++++++++ test/policies/guts/navigation_policy_test.rb | 12 +++++++++ test/policies/guts/option_policy_test.rb | 12 +++++++++ test/policies/guts/permission_policy_test.rb | 12 +++++++++ test/policies/guts/site_policy_test.rb | 12 +++++++++ test/policies/guts/type_policy_test.rb | 12 +++++++++ test/policies/guts/user_policy_test.rb | 12 +++++++++ test/support/guts/policy_test_support.rb | 18 +++++++++++-- 37 files changed, 253 insertions(+), 96 deletions(-) delete mode 100644 test/helpers/permissions_helper_test.rb create mode 100644 test/policies/guts/application_policy_test.rb create mode 100644 test/policies/guts/content_policy_test.rb create mode 100644 test/policies/guts/group_policy_test.rb create mode 100644 test/policies/guts/index_policy_test.rb create mode 100644 test/policies/guts/medium_policy_test.rb create mode 100644 test/policies/guts/metafield_policy_test.rb create mode 100644 test/policies/guts/navigation_item_policy.rb create mode 100644 test/policies/guts/navigation_policy_test.rb create mode 100644 test/policies/guts/option_policy_test.rb create mode 100644 test/policies/guts/permission_policy_test.rb create mode 100644 test/policies/guts/site_policy_test.rb create mode 100644 test/policies/guts/type_policy_test.rb create mode 100644 test/policies/guts/user_policy_test.rb diff --git a/app/concerns/guts/granted_concern.rb b/app/concerns/guts/granted_concern.rb index f6cc48f..9e54665 100644 --- a/app/concerns/guts/granted_concern.rb +++ b/app/concerns/guts/granted_concern.rb @@ -32,13 +32,13 @@ def grant_resource_string(resource) # @note This is from Pundit's finder def grant_class_name(subject) if subject.respond_to?(:model_name) - subject.model_name + subject.model_name.to_s elsif subject.is_a?(Symbol) subject.to_s.camelize elsif subject.is_a?(Class) - subject + subject.to_s else - subject.class + subject.class.to_s end end end diff --git a/app/helpers/guts/categories_helper.rb b/app/helpers/guts/categories_helper.rb index 88f8b31..33d3afd 100644 --- a/app/helpers/guts/categories_helper.rb +++ b/app/helpers/guts/categories_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for categories - module CategoriesHelper - end + module CategoriesHelper; end end diff --git a/app/helpers/guts/contents_helper.rb b/app/helpers/guts/contents_helper.rb index 4bc079f..e515541 100644 --- a/app/helpers/guts/contents_helper.rb +++ b/app/helpers/guts/contents_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for contents - module ContentsHelper - end + module ContentsHelper; end end diff --git a/app/helpers/guts/groups_helper.rb b/app/helpers/guts/groups_helper.rb index fc3548e..dbc7054 100644 --- a/app/helpers/guts/groups_helper.rb +++ b/app/helpers/guts/groups_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for groups - module GroupsHelper - end + module GroupsHelper; end end diff --git a/app/helpers/guts/index_helper.rb b/app/helpers/guts/index_helper.rb index 9a5b204..e132321 100644 --- a/app/helpers/guts/index_helper.rb +++ b/app/helpers/guts/index_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for index - module IndexHelper - end + module IndexHelper; end end diff --git a/app/helpers/guts/media_helper.rb b/app/helpers/guts/media_helper.rb index a65ff3d..156db50 100644 --- a/app/helpers/guts/media_helper.rb +++ b/app/helpers/guts/media_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for media - module MediaHelper - end + module MediaHelper; end end diff --git a/app/helpers/guts/metafields_helper.rb b/app/helpers/guts/metafields_helper.rb index df780e2..cb312c6 100644 --- a/app/helpers/guts/metafields_helper.rb +++ b/app/helpers/guts/metafields_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for metafields - module MetafieldsHelper - end + module MetafieldsHelper; end end diff --git a/app/helpers/guts/navigation_items_helper.rb b/app/helpers/guts/navigation_items_helper.rb index 40e87b7..fb4ab83 100644 --- a/app/helpers/guts/navigation_items_helper.rb +++ b/app/helpers/guts/navigation_items_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for navigation items. - module NavigationItemsHelper - end + module NavigationItemsHelper; end end diff --git a/app/helpers/guts/navigations_helper.rb b/app/helpers/guts/navigations_helper.rb index 28a55ba..132b315 100644 --- a/app/helpers/guts/navigations_helper.rb +++ b/app/helpers/guts/navigations_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for navigations - module NavigationsHelper - end + module NavigationsHelper; end end diff --git a/app/helpers/guts/options_helper.rb b/app/helpers/guts/options_helper.rb index 5f3d37f..a194a92 100644 --- a/app/helpers/guts/options_helper.rb +++ b/app/helpers/guts/options_helper.rb @@ -1,5 +1,4 @@ module Guts # Options helper - module OptionsHelper - end + module OptionsHelper; end end diff --git a/app/helpers/guts/permissions_helper.rb b/app/helpers/guts/permissions_helper.rb index 962e557..339d305 100644 --- a/app/helpers/guts/permissions_helper.rb +++ b/app/helpers/guts/permissions_helper.rb @@ -1,11 +1,4 @@ module Guts # Permissions helper - module PermissionsHelper - # Removes Guts from the authorization string to pretty it - # @param [String] item the item to clean - # @return [String] the cleaned string - def clean_subject_class(item) - item.gsub(/Guts::/, '').titleize - end - end + module PermissionsHelper; end end diff --git a/app/helpers/guts/sites_helper.rb b/app/helpers/guts/sites_helper.rb index 31a88c2..87f9180 100644 --- a/app/helpers/guts/sites_helper.rb +++ b/app/helpers/guts/sites_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for sites - module SitesHelper - end + module SitesHelper; end end diff --git a/app/helpers/guts/types_helper.rb b/app/helpers/guts/types_helper.rb index 76a0ffc..50df005 100644 --- a/app/helpers/guts/types_helper.rb +++ b/app/helpers/guts/types_helper.rb @@ -1,5 +1,4 @@ module Guts # View helpers for types - module TypesHelper - end + module TypesHelper; end end diff --git a/app/helpers/guts/users_helper.rb b/app/helpers/guts/users_helper.rb index 8bf0182..aea7f22 100644 --- a/app/helpers/guts/users_helper.rb +++ b/app/helpers/guts/users_helper.rb @@ -3,11 +3,13 @@ module Guts module UsersHelper # Gets the user's gravatar # @param [Object] user the user's object + # @param [Integer] size the size to fetch # @return [String] the image HTML # @note Sets a class of `gravatar` to the image HTML - def gravatar_for(user) + def gravatar_for(user, size = nil) gravatar_id = Digest::MD5.hexdigest user.email.downcase gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" + gravatar_url << "?s=#{size}" unless size.nil? image_tag gravatar_url, alt: user.name, class: 'gravatar' end diff --git a/app/models/guts/permission.rb b/app/models/guts/permission.rb index 2b91cac..bf1d7e1 100644 --- a/app/models/guts/permission.rb +++ b/app/models/guts/permission.rb @@ -5,5 +5,6 @@ class Permission < ApplicationRecord validates :resource, presence: true validates :grant, presence: true + validates_uniqueness_of :permissionable_id, scope: %i(permissionable_type resource grant) end end diff --git a/test/controllers/guts/application_controller_with_multisite_test.rb b/test/controllers/guts/application_controller_with_multisite_test.rb index 422d2a5..34e04da 100644 --- a/test/controllers/guts/application_controller_with_multisite_test.rb +++ b/test/controllers/guts/application_controller_with_multisite_test.rb @@ -9,15 +9,15 @@ class ApplicationControllerWithMultisiteTest < ActionController::TestCase test 'should set current site and current site should be nil' do get :index - assert_nil assigns('current_site') + assert_nil assigns(:current_site) end test 'should set current site with actual site' do - @request.host = 'fr.testsite.com' + @request.host = 'fr.testsite.com' # From fixture get :index - assert_instance_of Guts::Site, assigns('current_site') - assert_equal @request.host, assigns('current_site').domain + assert_instance_of Guts::Site, assigns(:current_site) + assert_equal @request.host, assigns(:current_site).domain end end end diff --git a/test/helpers/permissions_helper_test.rb b/test/helpers/permissions_helper_test.rb deleted file mode 100644 index 7446def..0000000 --- a/test/helpers/permissions_helper_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -module Guts - class PermissionsHelperTest < ActionView::TestCase - test 'returns cleaned subject class' do - assert_equal 'Navigation Item', clean_subject_class('Guts::NavigationItem') - end - end -end diff --git a/test/helpers/users_helper_test.rb b/test/helpers/users_helper_test.rb index 7043e73..6be5c62 100644 --- a/test/helpers/users_helper_test.rb +++ b/test/helpers/users_helper_test.rb @@ -2,18 +2,29 @@ module Guts class UsersHelperTest < ActionView::TestCase - test 'returns a gravatar image' do - user = guts_users :admin_user - + setup do + @user = guts_users :admin_user + stub_request(:get, %r{https:\/\/secure.gravatar.com\/avatar\/.*}).to_return(body: '') - - gravatar = gravatar_for user + end + + test 'returns a gravatar image' do + gravatar = gravatar_for @user html = Nokogiri::HTML(gravatar).xpath('//img') - email_md5 = Digest::MD5.hexdigest(user.email.downcase) - + email_md5 = Digest::MD5.hexdigest(@user.email.downcase) + assert_equal 'gravatar', html.attr('class').value - assert_equal user.name, html.attr('alt').value + assert_equal @user.name, html.attr('alt').value assert_equal "https://secure.gravatar.com/avatar/#{email_md5}", html.attr('src').value end + + test 'returns a gravatar image with size' do + @user = guts_users :admin_user + gravatar = gravatar_for @user, 500 + html = Nokogiri::HTML(gravatar).xpath('//img') + email_md5 = Digest::MD5.hexdigest(@user.email.downcase) + + assert_equal "https://secure.gravatar.com/avatar/#{email_md5}?s=500", html.attr('src').value + end end end diff --git a/test/mailers/guts/user_mailer_test.rb b/test/mailers/guts/user_mailer_test.rb index 6b48775..4d405f5 100644 --- a/test/mailers/guts/user_mailer_test.rb +++ b/test/mailers/guts/user_mailer_test.rb @@ -1,14 +1,11 @@ class UserMailerTest < ActionMailer::TestCase - setup do - @user = guts_users :admin_user - end - test 'should send reset' do - email = Guts::UserMailer.password_reset(@user).deliver_now + user = guts_users :admin_user + email = Guts::UserMailer.password_reset(user).deliver_now assert_not ActionMailer::Base.deliveries.empty? assert_equal ['from@example.com'], email.from - assert_equal [@user.email], email.to + assert_equal [user.email], email.to assert_equal 'Reset Password', email.subject assert_match %r{/reset_password\/[0-9a-zA-Z]+}, email.text_part.body.to_s assert_match %r{/reset_password\/[0-9a-zA-Z]+}, email.html_part.body.to_s diff --git a/test/models/guts/group_test.rb b/test/models/guts/group_test.rb index fbe3af3..5ad1628 100644 --- a/test/models/guts/group_test.rb +++ b/test/models/guts/group_test.rb @@ -35,19 +35,6 @@ class GroupTest < ActiveSupport::TestCase assert_operator group.metafields.size, :>, 0 end - test 'should determine grant resource string' do - group = guts_groups :admins - result = group.send :grant_resource_string, %i(guts navigation_item) - result2 = group.send :grant_resource_string, %i(guts type) - result3 = group.send :grant_resource_string, Guts::User - result4 = group.send :grant_resource_string, group - - assert_equal 'Guts::NavigationItem', result - assert_equal 'Guts::Type', result2 - assert_equal 'Guts::User', result3 - assert_equal 'Guts::Group', result4 - end - test 'should check grants' do group = guts_groups :admins diff --git a/test/models/guts/permission_test.rb b/test/models/guts/permission_test.rb index f42791c..1bcb6be 100644 --- a/test/models/guts/permission_test.rb +++ b/test/models/guts/permission_test.rb @@ -30,5 +30,27 @@ class PermissionTest < ActiveSupport::TestCase assert_not permission.save assert_not permission_2.save end + + test 'permissions for permissionable should be unique' do + permission = Permission.new( + permissionable: guts_users(:admin_user), + resource: 'guts_user', + grant: 'index' + ) + permission2 = Permission.new( + permissionable: guts_users(:admin_user), + resource: 'guts_user', + grant: 'index' + ) + permission3 = Permission.new( + permissionable: guts_users(:regular_user), + resource: 'guts_user', + grant: 'index' + ) + + assert permission.save + assert_not permission2.save + assert permission3.save + end end end diff --git a/test/models/guts/user_test.rb b/test/models/guts/user_test.rb index 79751bb..c353388 100644 --- a/test/models/guts/user_test.rb +++ b/test/models/guts/user_test.rb @@ -61,23 +61,6 @@ class UserTest < ActiveSupport::TestCase assert_operator user.metafields.size, :>, 0 end - test 'should determine grant resource string' do - user = guts_users :admin_user - result = user.send :grant_resource_string, %i(guts navigation_item) - result2 = user.send :grant_resource_string, %i(guts type) - result3 = user.send :grant_resource_string, Guts::User - result4 = user.send :grant_resource_string, user - result5 = user.send :grant_resource_string, Hash - result6 = user.send :grant_resource_string, 'Boo' - - assert_equal 'Guts::NavigationItem', result - assert_equal 'Guts::Type', result2 - assert_equal 'Guts::User', result3 - assert_equal 'Guts::User', result4 - assert_equal Hash, result5 - assert_equal String, result6 - end - test 'should check grants' do user = guts_users :admin_user diff --git a/test/policies/guts/application_policy_test.rb b/test/policies/guts/application_policy_test.rb new file mode 100644 index 0000000..73a6ba0 --- /dev/null +++ b/test/policies/guts/application_policy_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +module Guts + class ApplicationPolicyTest < PolicyTest + test 'granted concern' do + user = guts_users :admin_user + result = user.send :grant_resource_string, %i(guts navigation_item) + result2 = user.send :grant_resource_string, %i(guts type) + result3 = user.send :grant_resource_string, Guts::User + result4 = user.send :grant_resource_string, user + result5 = user.send :grant_resource_string, Hash + result6 = user.send :grant_resource_string, 'Boo' + + assert_equal 'Guts::NavigationItem', result + assert_equal 'Guts::Type', result2 + assert_equal 'Guts::User', result3 + assert_equal 'Guts::User', result4 + assert_equal 'Hash', result5 + assert_equal 'String', result6 + end + end +end \ No newline at end of file diff --git a/test/policies/guts/category_policy_test.rb b/test/policies/guts/category_policy_test.rb index ac7a3d2..dc483f8 100644 --- a/test/policies/guts/category_policy_test.rb +++ b/test/policies/guts/category_policy_test.rb @@ -2,9 +2,11 @@ module Guts class CategoryPolicyTest < PolicyTest - def test_new - user = guts_users :admin_user - assert permit(user, Category.new, :new) + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end end end end \ No newline at end of file diff --git a/test/policies/guts/content_policy_test.rb b/test/policies/guts/content_policy_test.rb new file mode 100644 index 0000000..4b4fb6e --- /dev/null +++ b/test/policies/guts/content_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class ContentPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/group_policy_test.rb b/test/policies/guts/group_policy_test.rb new file mode 100644 index 0000000..12f7ee5 --- /dev/null +++ b/test/policies/guts/group_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class GroupPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/index_policy_test.rb b/test/policies/guts/index_policy_test.rb new file mode 100644 index 0000000..fc6cd91 --- /dev/null +++ b/test/policies/guts/index_policy_test.rb @@ -0,0 +1,11 @@ +require 'test_helper' + +module Guts + class IndexPolicyTest < PolicyTest + test 'index method can be accessed by all who are logged in' do + assert permit(guts_users(:admin_user), nil, :index) + assert permit(guts_users(:regular_user), nil, :index) + assert_not permit(nil, nil, :index) + end + end +end \ No newline at end of file diff --git a/test/policies/guts/medium_policy_test.rb b/test/policies/guts/medium_policy_test.rb new file mode 100644 index 0000000..27ecbf1 --- /dev/null +++ b/test/policies/guts/medium_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class MediumPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/metafield_policy_test.rb b/test/policies/guts/metafield_policy_test.rb new file mode 100644 index 0000000..0b469e7 --- /dev/null +++ b/test/policies/guts/metafield_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class MetafieldPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/navigation_item_policy.rb b/test/policies/guts/navigation_item_policy.rb new file mode 100644 index 0000000..da67022 --- /dev/null +++ b/test/policies/guts/navigation_item_policy.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class NavigationItemPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/navigation_policy_test.rb b/test/policies/guts/navigation_policy_test.rb new file mode 100644 index 0000000..01bba43 --- /dev/null +++ b/test/policies/guts/navigation_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class NavigationPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/option_policy_test.rb b/test/policies/guts/option_policy_test.rb new file mode 100644 index 0000000..1cd6a48 --- /dev/null +++ b/test/policies/guts/option_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class OptionPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/permission_policy_test.rb b/test/policies/guts/permission_policy_test.rb new file mode 100644 index 0000000..903120d --- /dev/null +++ b/test/policies/guts/permission_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class PermissionPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/site_policy_test.rb b/test/policies/guts/site_policy_test.rb new file mode 100644 index 0000000..2735365 --- /dev/null +++ b/test/policies/guts/site_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class SitePolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/type_policy_test.rb b/test/policies/guts/type_policy_test.rb new file mode 100644 index 0000000..c460c8b --- /dev/null +++ b/test/policies/guts/type_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class TypePolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/policies/guts/user_policy_test.rb b/test/policies/guts/user_policy_test.rb new file mode 100644 index 0000000..a9bc6e4 --- /dev/null +++ b/test/policies/guts/user_policy_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +module Guts + class UserPolicyTest < PolicyTest + test 'all methods' do + %i(index new create update edit destroy).each do |method| + assert policy_permit(guts_users(:admin_user), method) + assert_not policy_permit(guts_users(:regular_user), method) + end + end + end +end \ No newline at end of file diff --git a/test/support/guts/policy_test_support.rb b/test/support/guts/policy_test_support.rb index 86b43a2..10faae8 100644 --- a/test/support/guts/policy_test_support.rb +++ b/test/support/guts/policy_test_support.rb @@ -2,10 +2,24 @@ # From http://stackoverflow.com/a/30754295 class PolicyTest < ActiveSupport::TestCase def permit(current_user, record, action) - self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?") + self + .class + .to_s + .gsub(/Test/, '') + .constantize + .new(current_user, record) + .public_send("#{action.to_s}?") end def forbid(current_user, record, action) !permit(current_user, record, action) end -end \ No newline at end of file + + def policy_permit(object, method) + permit( + object, + self.class.to_s.gsub(/PolicyTest/, '').constantize, + method + ) + end +end