oivoodoo / devise_masquerade

Extension for devise, enable login as functionality. Add link to the masquerade_path(resource) and use it.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Incompatibility with custom to_param (FriendlyID, etc)

marckohlbrugge opened this issue · comments

This gem uses to_param as an identifier for the resource.

session[skey] = resource_obj.to_param

But then uses the id to retrieve the resource:

masquerading_resource_class.find(resource_id)

This breaks any implementation where a resource's to_param does not equal its id. For example:

class User
  def to_param
    username
  end
end

To fix this, I suggest either:

A) replacing the .to_param call with a simple .id, or
B) allowing for easy overriding of masquerading_resource_class.find(resource_id) (e.g. by extracting it into a helper method).

For anyone needing a quick fix:

You can create custom Masquerades controller that overrides find_owner_resource like so:

  def find_owner_resource(masqueradable_resource)
    skey = session_key(masqueradable_resource, masquerading_guid)

    if Devise.masquerade_storage_method_session?
      resource_id = session[skey]

      # ⬇️⬇️⬇️ Make the necessary changes here ⬇️⬇️⬇️
      masquerading_resource_class.friendly.find(resource_id)
    else
      data = Rails.cache.read(skey)

      GlobalID::Locator.locate_signed(data, for: "masquerade")
    end
  end
commented

@marckohlbrugge I think friendly_id changed the approaches for the last years. I remember the time when .find(param) worked without scoping friendly.

commented

@marckohlbrugge is it possible to set default scope instead for the user model?

default_scope { friendly }

not sure if it works.

@oivoodoo Yes, FriendlyID allows you to globally override the .find method.

I personally don't use that as overriding .find can introduce unexpected behavior throughout the app. Furthermore, I don't use FriendlyId everywhere I use a custom to_param

I think the bigger issue here is that the gem uses resource_obj.to_param but then assumes that's always going to work with .find. The obvious solution to me seem to replace resource_obj.to_param with resource_obj.id

This also fixes a potential bug where a username etc (returned by to_param) might changing during a masquerade session rendering the session[skey] value broken. Whereas when storing an id there's no such issue. (Which is also why Devise etc store id's, rather than calling to_param)

commented

@marckohlbrugge reasonable to apply the change to_param -> id. Going to change it. Thank you!

commented

@marckohlbrugge released 2.1.0

@oivoodoo Thank you 🙌