不知道为什么会在这里发生
PostsController#update中的NoMethodError nil:NilClass的未定义方法“用户”
我的用户具有admin:true,并且我无法更新其他users.posts。
我想让所有用户看到内容,但是只有注册用户才能创建内容。如果用户是管理员或该内容的创建者,他也可以对其进行编辑/更新/销毁。
post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { minimum: 5 }
validates :body, presence: true, length: { minimum: 240 }
end
user.rb
class User < ApplicationRecord
include Encryptable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_attached_file :avatar
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
validates :level, numericality: { less_than_or_equal_to: 100, only_integer: true }, allow_blank: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
before_validation :downcase_email #, :populate_iv_fields #if you need/want iv to change more often
before_create :create_encryption_key
after_create :save_encryption_key
after_create :build_user_consents
attr_encrypted :email, key: :encryption_key
has_many :user_consents, dependent: :destroy
#entry point for exporting user's personal information
def self.export_personal_information(user_id)
return nil unless User.exists?(user_id)
descendants = ApplicationRecord.descendants.reject{|model| !model.has_personal_information?}
result = Hash.new
descendants.each do |descendant|
result[descendant.name] = descendant.export_personal_information_from_model(user_id)
end
return result
end
#simplest example, we just export to json
def self.export_personal_information_from_model(user_id)
return User.find(user_id).to_json
end
#overwrite this to true for methods that you will want to be included in export_personal_information
def self.has_personal_information?
true
end
#helper method if you are creating a user from console and want them to have all consents set
def fill_consents
hash = Hash.new
ConsentCategory.all.map(&:id).each do |id|
hash[id]='on'
end
self.registration_consents=hash
end
#unfortunately not having an email field that you can just "write to" breaks
#Devise. Below some necessary workarounds
def email_changed?
encrypted_email_changed?
end
def downcase_email
self.email = self.email.downcase
end
def registration_consents=(consents)
@consents = consents
end
def registration_consents
@consents
end
validate :validate_consents_completeness
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :username, allow_blank: false, if: :username_changed?
validates_length_of :username, within: 6..20, allow_blank: true
validate :validate_email_uniqueness #validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: Devise.email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: Devise.password_length, allow_blank: true
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
#end original devise
def email_changed?
self.encrypted_email_changed?
end
def email_required?
true
end
def email_unique?
records = Array(self.class.find_by_email(self.email))
records.reject{|u| self.persisted? && u.id == self.id}.empty?
end
#unfortunately, this is an O(n) operation now that has to go through ALL the users to see if an email is unique. Sorry!
#if you need it to ne O(1) then consider adding email_hash field instead
def self.find_by_email(email)
users = User.all
users.each do |user|
return user if user.email.downcase == email.downcase
end
return nil
end
protected
def validate_email_uniqueness
errors.add(:email, :taken) unless email_unique?
end
def validate_consents_completeness
return if self.id #we assume that already created user has all consents
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless self.registration_consents
consents = ConsentCategory.where(mandatory: true).map(&:id)
ids = self.registration_consents.keys.map(&:to_i) #we are relying on a fact that checkboxes that are not checked are not sent to Rails back-end at all
consents.each do |consent_type|
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless ids.include?(consent_type)
end
end
def build_user_consents
ids = registration_consents.keys
categories = ConsentCategory.where(id: ids)
raise 'Die vom Benutzer eingereichte Zustimmungsliste enthält Objekte, die nicht in der Datenbank vorhanden sind!' if categories.count != ids.count
categories.each do |category|
consent = UserConsent.new
consent.consent_category = category
consent.user = self
consent.requires_revalidation = false
consent.agreed_at = self.created_at
consent.save!
end
end
end
post_policy.rb
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if post.user_id == user.id || user == user.admin?
end
def destroy?
return true if post.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
user.admin?
end
def edit?
user.admin?
end
def destroy?
user.admin?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end
post_controller
class PostsController < ApplicationController
before_action :find_post, only: %i[destroy edit update comment_owner upvote downvote]
after_action :verify_authorized, except: [:index, :show]
layout '_app_nav'
def index
return redirect_to post_path(params[:post_id]) if params[:post_id]
return redirect_to user_path(params[:user_id]) if params[:user_id]
@post = Post.all.order('created_at DESC')
@posts = Post.all.order('created_at DESC')
@user = User.all
@posts = if params[:suche]
else
Post.all.order('created_at DESC')
end
@comments = Comment.all
end
def new
@post = Post.new
end
def create
@post = current_user.posts.build(post_params)
authorize @post
if @post.save!
redirect_to @post
else
render 'new'
end
end
def show
@post = Post.find(params[:id])
@user = @post.user
@comments = Comment.where(post_id: @post).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def edit
authorize @post
@post = Post.find(params[:id])
end
def update
@user = User.find(params[:id])
@post = Post.find(params[:id])
authorize @post
@post.update(title: params[:title], body: params[:post_params])
redirect_to post_path(@post)
end
def destroy
@post = Post.find(params[:id])
@post.destroy
authorize @post
redirect_to posts_path
end
=begin
def upvote
@post.upvote_from current_user
redirect_to post_path(@post)
end
def downvote
@post.downvote_from current_user
redirect_to post_path(@post)
end
=end
private
def post_params
params.require(:post).permit(:title, :body, :user_id)
end
def find_post
@post = Post.find(params[:id])
end
end
application_controller
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :authenticate_user!
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit(:username, :email, :password, :password_confirmation, registration_consents: {})
end
end
private
def user_not_authorized
flash[:alert] = 'test'
redirect_to(root_path)
end
end
registrations_controller:
class RegistrationsController < Devise::RegistrationsController
private
def account_update_params
params.require(:user).permit(:email, :username, :avatar, :current_password, :password, :password_confirmation)
end
end
编辑:
使用@post(例如return true if user.present? && user == @post.user || user == user.admin?
)更新我的post_policy.rb可以解决-> PostsController#update中的Pundit :: NotAuthorizedError
不允许更新?
答案 0 :(得分:0)
如果您使用的是devise,则可以在application_controller.rb中使用此回调来验证您是否已登录用户。
before_action:authenticate_user!
避免这样做
user.present?
您的post_policy.rb应该看起来像这样
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if record.user_id == user.id || user == user.admin?
end
def destroy?
return true if record.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
此外,为避免用户可以在浏览器中输入链接,您可以对控制器执行额外的步骤,即添加以下回调和方法
class PostsController < ApplicationControl
before_action :authorization
private
def authorization
authorize(Post)
end
end
编辑:
确保您的ApplicationController看起来像这样,以防止出现Pundit :: NotAuthorizedError错误。
class ApplicationController < ActionController::Base
include Pundit
before_action :authenticate_user!
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to(root_path)
end
end
答案 1 :(得分:0)
在您的PostsController
中,您需要在数组中包含update
方法,您可以在其中指定在执行操作之前应该authenticate_user
使用哪些方法:
before_action :authenticate_user!, only: [:create, :destroy, :new, :edit, :update]