So I am running into a problem where I have two separate devise models (Admin and User), and because I am using a multi tenancy gem called Milia I need to two application controllers (One for the Admin which is outside of the tenancy and one for the User which is inside the tenancy. The problem is I need to access one controller from both of those devise models, but I can only inherit that controller from one of the below application controllers. Is there a way around this where I can specify which user should use which application controller when hitting the controller for a particular resource? Or is there another way around this problem?
The two application controllers look like this
class AdminApplicationController < ActionController::Base
before_action :authenticate_admin!
end
class ApplicationController < ActionController::Base
before_action :authenticate_tenant!
end
Give you some inspiration :)
class ApplicationController < ActionController::Base
before_action :authenticate_them
def authenticate_them
authenticate_tenant!
end
end
class AdminApplicationController < ApplicationController
before_action :authenticate_them
def authenticate_them
authenticate_admin!
end
end
class AnotherController < ApplicationController
before_action :authenticate_them
def authenticate_them
if admin_signed_in? or tenant_signed_in?
#or whatever other authentication you want
end
end
end
Related
I am learning to use Pundit for authorization. But the way I see it is authorization for resources not pages. I want a user to be redirected to a unauthorized page if he/she is not authorized to visit the page using pundit.
For e.g.
class OnlyAdminCanVisitController < ApplicationController
before_filter :admin_authenticate
Stops a non-admin role user.
Also, I want to take care of made up scenarios like following(Considering there are 4 roles as Admin,Manager,Employee,Outsider. The design below is obiviously bad)
class AdminManagerCanVisitController < ApplicationController
before_filter :admin_or_manager_authenticate
class AdminEmployeeCanVisitController < ApplicationController
before_filter :admin_or_employee_authenticate
class AdminOutsiderCanVisitController < ApplicationController
before_filter :admin_or_outsider_authenticate
class AdminManagerEmployeeCanVisitController < ApplicationController
before_filter :admin_or_manager_employee_authenticate
I have 4 roles and would like to write pundit policies for these controllers which allows any combination of authorizations.
Let me know if pundit is designed to tackle this issue.
Thanks
There is not much difference between pages and resources actually. So you can solve your problem by rescuing a denied Authorization from your application_controller.rb :
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
protected
def admin_authenticate
authorize current_user, :admin?
end
private
def user_not_authorized(exception)
# Redirect to whatever page you want if not authorized
end
end
You then need to define your policy. I generally create an admin? method in application_policy.rb (https://github.com/elabs/pundit#policies) so it is spread on my other policies as well :
class ApplicationPolicy
def admin?
# Logic to ensure the user is an admin
end
end
class UserPolicy < ApplicationPolicy
end
Then in your other controllers, just do as you did :
class OnlyAdminCanVisitController < ApplicationController
before_action :admin_authenticate
end
I'm making a control panel (user accounts) in rails.
in the layout I need to show things like messages or notifications (facebook-like style).
the problem is these things require an access to database and I'm not sure where to put this code because it's not related to a controller, but the layout is shared with multiple controlers.
so where is the best place to put the code to fetch messages from database should I put in the layout itself (I don't think its right), or as helper ?
the best solution was to build a control-panel controller which handles authentication and permissions and loads common user data from database such as messages...
here is an example code
class ControlPanelController < ApplicationController
before_filter :authenticate_user!
before_filter :get_user_data
helper_method :mailbox
authorize_resource
protected
def get_user_data
#header_conversations=mailbox.inbox.limit(3)
#uevents= Event.scoped
#uevents= #uevents.after(Time.now)
end
def mailbox
#mailbox ||= current_user.mailbox
end
end
and then all the classes in my web application extends this class :)
I found a way is to use before_filter. by defining the filter in the ApplicationController (so that you can access it from any controller).
class ApplicationController < ActionController::Base
# ..
protected
def load_messages
#messages = Message.all
end
end
and then in the any controller:
class FooController < ApplicationController
before_filter :load_messages
def index
# #messages is set
end
end
I have a separete part of the site for separate, dedicated customers, they have tools under /dedicated path, and all controllers they have are inheritences of DedicatedController.
I want to create a before_filter in application_controller to protect this customers from opening any other pages that are not in controllers that inherited by dedicated_controlle.
If you use a before_filter in the ApplicationController to prevent customers from going to pages there you can use skip_filter in the base controller for the DedicatedController.
So for ours we have:
class ApplicationController
before_filter :ensure_not_a_customer
.
.
end
class Admin::BaseController < ApplicationController
skip_filter :ensure_not_a_customer
.
.
end
class Admin::WebpageController < Admin::BaseController
.
.
end
Then anything inherited from Admin::BaseController will skip the before_filter from the ApplicationController.
So I have a method and corresponding partial for including a set of random photos in the sidebar of certain areas of our site.
Right now I have a random_photos method in ApplicationController set with a before_filter.
That works in the sense that it makes the contents of the random_photos method available wherever I need it, but it also unnecessarily executes some complex SQL queries when I don't know it too (ie, when I don't need to access those random photos).
So, how can I limit the accessing of the random_photos method to only when I really need it?
You can add an :if condition to the before_filter call, like so:
class ApplicationController < ActiveController::Base
before_filter :random_photos, :if => is_it_the_right_time?
Yet another option is to use skip_before_filter. It just depends in how many controllers you want to be different. Use skip_before_filter if there are only a handful of controllers you want to be the exception. Use one of the other suggestions if there are many controllers where you want to bypass the filter.
class ApplicationController < ActiveController::Base
before_filter :random_photos
def random_photos
#photos = Photo.random
end
end
class OtherController < ApplicationController
skip_before_filter :random_photos
...
end
You can keep the random_photos method in ApplicationController, and put the before_filters in your other controllers.
class ApplicationController < ActiveController::Base
...
def random_photos
#photos = Photo.random
end
end
class OtherController < ApplicationController
before_filter :random_photos, :only => 'show'
...
end
It depends on how many functions are making use of random_photos...
If a handful then use vrish88's approach but with an after_filter:
class ApplicationController < ActiveController::Base
after_filter :random_photos, :if => is_it_the_right_time?
...
private
def is_it_the_right_time?
return #get_random_photos
end
end
class SomeController < ApplicationController
def show
#get_random_photos = true
...
end
end
If every function in a controller will make use of it then use the skip_before_filter or move the before_filter in the controller and out of the application controller.
Many ways to get it done, and none is more correct then the next. Just try to keep it as simple and transparent as possible so you don't recreate the functionality months down the road because you forgot where all the pieces are located.
I am trying to set the following up:
www.domain.com goes to the main site (user can register, information about application)
foo.domain.com goes to the main application that is customized to the user(subdomain)
what is the best way to separate the rails parts? Namespaced controllers seem frowned upon.
Don't separate them. Just use a before_filter to require login and a subdomain on the controllers that requires a subdomain/client to be present.
class ApplicationController < ActionController::Base
private
def require_subdomain_scope
# check if request.subdomains is blank or www. Something like that.
end
end
class StaticPagesController < ApplicationController
# no before_filter!
end
class ProjectsController < ApplicationController
before_filter :require_login, :require_subdomain_scope
end