简体   繁体   中英

Rails, Devise, Pundit - authorise Profile created from Devise registration controller

Feel free to say if you think something is wrong. I extended Devise Registration controller to create a Profile object to every new user:

class Users::RegistrationsController < Devise::RegistrationsController

  def new
    resource = build_resource({})
    resource.profile = Profile.new
    resource.profile.user_id = @user.id
    respond_with resource 
  end

They both are has_one - has_one related and in database:

create_table :profiles do |t|
  t.belongs_to :user, index: { unique: true }, foreign_key: true
end

So to get the right profile of current user, I must:

private 
  def set_profile
    @profile = Profile.where(user_id: current_user.id).first 
  end

And this kinda solves the problem - seems other users cant go around this query and access other profiles (or CAN THEY?), but for other resources I use Pundit to control authorisation, so now it feels a bit messy.

So thats one concern. Other - I still don't know how to act when there is no user logged, because if visiting any restricted resource, this:

private
 def set_some_resource
 end
end

Throws - "undefined method `id' for nil:NilClass) - how is best to avoid this?

Thanks for any advices.

You may want to start by reading the Rails guides on assocations .

To create a one to one association you use belongs_to on the side with the foreign key column and has_one on the other.

class User
  has_one :profile
end

class Profile
  belongs_to :user
end

ActiveRecord then automatically links the records together. In general you should avoid setting ids (or getting associated records by ids) explicitly and instead use the assocations:

class Users::RegistrationsController < Devise::RegistrationsController
  # ...
  def new
    # calls Devise::RegistrationsController#new 
    super do |user|
      user.profile.new
    end
  end
end

Devise is pretty nifty and lets you pass a block to tap into the flow instead of copypasting the whole action.

Simularily you would fetch the current users profile with:

private 
  def set_profile
    @profile = current_user.profile
  end

You can set if the callback should be called by using the if: option.

before_action :set_profile, if: :user_signed_in?

But if the action requires authentication you should make sure that it is after :authenticate_user! anyways which will halt the filter chain.

And this kinda solves the problem - seems other users cant go around this query and access other profiles (or CAN THEY?), but for other resources I use Pundit to control authorisation, so now it feels a bit messy.

You don't need to use Pundit to authorize creating a profile or fetching the current users profile. Since the profile is fetched via the user the is no way for another user to access it (well without hacking).

what you might want to authorize is the show, index, edit etc actions if you create a ProfilesController .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM