I have a (relatively) large rails app which uses declarative authorization for role based permissions. Admin users currently have the following permissions:
role :administrator do
has_omnipotence
end
I need to add a higher role (root) which can exclusively have permissions on certain actions.
The obvious thing to do is to get rid of has_omnipotence from the administrator role block and manually add all permissions on all controllers but root_accounts, but this is painful. Is there a way that I could do something like the following?
role :root do
has_permission_on [:root_accounts], :to => [:new, :create ... ]
end
role :administrator do
has_omnipotence {except [:root_accounts], :to => [:new, :create ...]}
end
There's no such syntax. But you can check for a specific role in your view/controller:
<% unless has_role?(:administrator) %>
<%= link_to 'New root account', new_root_account_path %>
<% end %>
or
def create
permission_denied if has_role?(:administrator)
...
end
It's not very scalable tho.
Related
Following is my setup
devise (4.4.3)
activeadmin (1.3.0)
Rails 5.2
I created a user table with devise. installed active admin and used the normal method and created admin_users table. NO I AM NOT USING THE SAME TABLE. THESE ARE DIFFERENT TABLES (I had to do this cause last time this question was marked as duplicate cause the user thought i used the same table)
Routes file looks like this:
devise_for :users
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
devise_scope :user do
authenticated :user do
root 'home#dashboard', as: :authenticated_root
end
unauthenticated do
root 'devise/sessions#new', as: :unauthenticated_root
end
end
Now the issue is root_path '/' redirects correctly and I am able to login as User. But I am not able to login as Admin. It redirects me back to root_path that is unauthenticated user.
But weirdly when I login as user, I can then login as admin_user.
AGAIN I AM USING DIFFERENT TABLES, admin_users and users
Thanks in advance.
I can tell you an easy way to add admin in devise
$rails g migration add_admin_to_user
in the db
def change
add_column :users, :admin , :boolean , {default: false}
end
in user.rb
def admin?
admin
end
<% if current_user && current_user.admin? %>
<% end %>
then i do sign up from the email that i want to be the admin
then in rails c select the email that you want it to be the admin for example
User.last
user=User.last
user.admin=true
user
user.save
Solved this.
So the issue is when you have activeadmin admin_users and users table that is created with devise, and you place the authenticated_user! before_action in application_controller, activeadmin interprets it as authorization failure(though this should not be the case).
Solution to this is, go to active_admin.rb and add the following code
config.skip_before_action :authenticate_user!
This forces activeadmin to listen to only its authrization_adapter.
I have gone through and looked at a bunch of screen casts (http://railscasts.com/episodes/270-authentication-in-rails-3-1) and tutorials (http://ruby.railstutorial.org/chapters/modeling-and-viewing-users-two?version=2.3#sec:secure_passwords) but I can't seem to find or apply what I am learning to create a single view password protected.
As of now I am generating a random password and showing it to the user to go back and view a file they have uploaded (generated using SecureRandom.hex). However, I know that using http_basic doesn't seem to work when I restrict it only to the show view and method in my controller : http_basic_authenticate_with :password => :passcode, :only => :show
I know that line of code does not work because :passcode does not reference the individual files password that was created.
Also, I know that http_basic_authenticate_with is not the way it should be done for secure passwords, so how would you go about this using has_secure_password etc?
You can try to encapsulate the http_basic_authenticate_with :password => :passcode in a method in your controller, and call the before_filter to call that method.
Something like that:
before_filter :restrict, :only => :show
def show
end
def restrict
http_basic_authenticate_with :password => :passcode
end
I am attempting to manage users in my app that uses Devise for the authentication. I followed the steps here in order to create a UsersController to allow me to do so. However, when I attempt to sign out, it says that the path /d/users/sign_out cannot be found. Is there anything else I need to add to get this to work?
EDIT: My routes look like:
devise_for :users. :path_prefix => 'd'
resources :users do
# stuff here
end
When I run rake routes, it gives me, for the destroy_user_session_path:
destroy_user_session DELETE /d/users/sign_out(.:format) {:controller=>"devise/sessions", :action=>"destroy"}
When I remove the path_prefix part, it attempts to 'show' a user with the ID sign_out
link_to "sign_out", destroy_user_session_path, method: :delete
do you have the method :delete in your view?
Given I have two models: company and location. A company has many locations.
I found Ryan Bates' Named Routes very interesting.
So I added resources for each of my model in routes.rb.
resources :companies
resources :locations
This enables me to generate links based on named routes like <%= link_to "Companies", companies_path %> which results in http://localhost:3000/companies.
Now I want to filter the list of locations based on the company they belong to. Before working with named routes I accomplished this by add a link like the following.
<%= link_to "Locations for this company", { :controller => 'locations', :action => 'list', :company_id => company.id } %>
Here I pass the company_id to the LocationsController which filters the location in its list action.
def list
#locations = Location.order("locations.id ASC").where(:company_id => #company.try(:id))
end
One option:
resources :companies do
get :list, :controller => :locations
end
will produce this route:
company_list GET /companies/:company_id/list(.:format) {:action=>"list", :controller=>"locations"}
And this provides you, then, with a company_list helper method.
This, however,
resources :companies do
get :list, :controller => :locations
resources :locations
end
Will also provide you with a company_locations helper that points,instead, to the index action on the LocationsController instead of the list.
company_locations GET /companies/:company_id/locations(.:format) {:action=>"index", :controller=>"locations"}
The latter is more RESTful, but you're the developer, so you get decide which naming helps you more.
The downside is that most of the actions/helpers on your Locations controller are now dependent on having a :company_id or instantiated Company model attached to them.
EDIT
3rd option:
resources :companies do
get :list, :controller => :locations
end
resources :locations
This seems best, since other than the 'company_list' route, you don't claim to have much need for locations being filtered by company.
Depending on what your location model needs to do, I would suggest using nested resources;
resources :companies do
resources :locations
end
Then do a rake routes, which will give you all sorts of new, nicer routes like:
company/id/locations
I have 3 type of users, 3 roles. With devise, I can get role with current_user.role method.
What is the cleanest way to make 3 different navigations (render some partial or whole layout) based on user's role?
If you want to redirect to different controllers and/or actions based on role, you may be interested in this question and answer: Rails 3 routing based on context.
I do this on this way:
<% if current_user.role? :admin %>
<li class="apanel"><%= link_to_unless_current("Administracioni panel", :controller => 'admin', :action => 'index') %></li>
<% end %>
It is a little stupid way, but works :)