Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rolify remove_role deletes from role table?

This is strange. I'm using Rolify + CanCan + Devise in my rails 3.2 app. My use case is simple. I want a user to have only one role at a time, thus to change a role, I do something like this:

user.remove_role "admin"
user.add_role "associate"

The strange thing to me is that when I do this, the role "admin" gets deleted from the Roles table. Why would this be? I don't want to eliminate the role entirely, just a given role from the user. What am I doing wrong?

Here's the SQL. Notice the last delete from roles statement:

3] pry(main)> u.remove_role "sub_admin"
  Role Load (0.1ms)  SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = 2 AND "roles"."name" = 'sub_admin'
   (0.0ms)  begin transaction
   (0.3ms)  DELETE FROM "users_roles" WHERE "users_roles"."user_id" = 2 AND "users_roles"."role_id" IN (2)
   (1.9ms)  commit transaction
  User Load (0.1ms)  SELECT "users".* FROM "users" INNER JOIN "users_roles" ON "users"."id" = "users_roles"."user_id" WHERE "users_roles"."role_id" = 2
   (0.0ms)  begin transaction
  SQL (2.1ms)  DELETE FROM "roles" WHERE "roles"."id" = ?  [["id", 2]]
   (0.6ms)  commit transaction
like image 734
user3009646 Avatar asked Nov 19 '13 16:11

user3009646


People also ask

How do I remove a role assignment in the REST API?

In the REST API, you remove a role assignment by using Role Assignments - Delete. Get the role assignment identifier (GUID). This identifier is returned when you first create the role assignment or you can get it by listing the role assignments.

How do I remove an access from a role?

To remove role assignments, you must have: Open Access control (IAM) at a scope, such as management group, subscription, resource group, or resource, where you want to remove access. Click the Role assignments tab to view all the role assignments at this scope.

How do I remove a role from an Azure resource?

Azure role-based access control (Azure RBAC) is the authorization system you use to manage access to Azure resources. To remove access from an Azure resource, you remove a role assignment. This article describes how to remove roles assignments using the Azure portal, Azure PowerShell, Azure CLI, and REST API.

How do I remove a role assignment from a security principal?

Click the Role assignments tab to view all the role assignments at this scope. In the list of role assignments, add a checkmark next to the security principal with the role assignment you want to remove. Click Remove. In the remove role assignment message that appears, click Yes.


2 Answers

The basic problem is that each combination if role-name, resource_type, and resource_id is stored only once in the roles table. If you delete this row, it is deleted for everyone.

The solution then is to delete only the rows from the join table of rolify connecting the User and the Role models. For ease of access i will make the join table a model to use some rails magic to generate the SQL. Since this is really a kind of service object, i will make it a class singleton. Here is my hack:

class UsersRoles < ActiveRecord::Base

    def self.delete_role(subject,role_symbol, obj=nil)
        res_name = obj.nil? ? nil : obj.class.name
        res_id   = obj.id rescue nil
        role_row = subject.roles.where(name: role_symbol.to_s, resource_type: res_name , resource_id: res_id).first
        if  role_row.nil?
            raise "cannot delete nonexisting role on subject"
        end
        role_id = role_row.id
        self.delete_all(user_id: subject.id,role_id: role_id)
    end

    private_class_method :new
end

This code is not optimized, but should give you idea what to do: for example you can now add a convenience method to the User model:

def delete_role(role_symbol,target=nil)
    UsersRoles.delete_role self,role_symbol,target
end

then you can say:

user.delete_role :admin

and it will only remove what you want.

Note that this will not remove the table row with the role, which I would retain for future use.

like image 177
semiomant Avatar answered Sep 23 '22 12:09

semiomant


I know this question is more than two years old, but I found better and more "rolify like" answer.
You can do it really easy in User model:

def remove_only_role_relation(role_name)
    roles.delete(roles.where(:name => role_name))
end

and use it like:

@user = User.find_by_id(params[:id])
role_name = params[:role_name]
# or:
# role_name = Role.find_by_id(params[:role_id]).name rescue nil
if @user and role_name
    @user.remove_only_role_relation(role_name)
end

I found similar code in their sources:
rolyfi: role.rb - here method remove_role cals method "remove" in adapter file: rolyfi: role_adapter.rb

like image 23
Eiji Avatar answered Sep 24 '22 12:09

Eiji