I'm trying to install the Rails Admin Gem using Sorcery for authentication instead of Devise.
Rails admin does provide a hook that you can use to attach your own authentication method. Here is the example they provide in their docs (using warden):
config.authenticate_with do
warden.authenticate! :scope => :admin
end
config.current_user_method { current_admin }
I'm guessing that inside the block I need to reference the before_filter that Sorcery uses to authenticate users, which would be require_login.
However, when I try that and I try to visit /admin when logged out, I get a routing error:
No route matches {:action=>"new", :controller=>"sessions"}
This probably happens because I am being redirected within the engine rather than in the main app.
How can I set this up correctly?
# config/initializers/rails_admin.rb
RailsAdmin.config do |config|
config.authenticate_with do
# Use sorcery's before filter to auth users
require_login
end
end
# app/controllers/application_controller.rb
class ApplicationController
# Overwrite the method sorcery calls when it
# detects a non-authenticated request.
def not_authenticated
# Make sure that we reference the route from the main app.
redirect_to main_app.login_path
end
end
#config/initializers/rails_admin.rb
RailsAdmin.config do |config|
...
config.parent_controller = 'ApplicationController'
end
If you use Sorcery with Cancancan gem, you should also add config.current_user_method(&:current_user) in your config/initializers/rails_admin.rb file, or you'll get the error: You are not authorized.
Related
I would like to secure the rails_admin pages using the sorcery gem. According to this SO answer, the way to do this is as follows:
# config/initializers/rails_admin.rb
RailsAdmin.config do |config|
config.authenticate_with do
# Use sorcery's before filter to auth users
require_login
end
end
# app/controllers/application_controller.rb
class ApplicationController
# Overwrite the method sorcery calls when it
# detects a non-authenticated request.
def not_authenticated
# Make sure that we reference the route from the main app.
redirect_to main_app.login_path
end
end
This overrides sorcery's default method for handling no login. The overriding does work in my app, but when I visit the rails_admin pages, I get the following error:
undefined local variable or method `root_path' for #<RailsAdmin::MainController.
so the overriding is not working in the rails_admin code. I am mounting rails_admin at the bottom of my routes file with
# config/routes.rb
...
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
How do I fix this?
It is because the rails_admin controller is not inheriting from my application controller. There is a rails_admin configuration setting that sets this inheritance, i.e.
#config/initializers/rails_admin.rb
RailsAdmin.config do |config|
...
config.parent_controller = 'ApplicationController'
end
How do you implement authentication with the rails_admin gem when you are not using devise e.g. you have rolled your own authentication?
In config/initializers/rails_admin.rb include a config.authenticate_with block and place your authentication logic there. It should raise an exception if the user is not authorised to use rails_admin. Here is a simple example:
RailsAdmin.config do |config|
config.authenticate_with do
raise 'You must be admin' unless signed_in? && current_user.admin?
end
end
If you want to follow the rails_admin instructions for the cancancan gem then also add the following config line:
config.current_user_method(&:current_user)
I have two devise models, user and admin, When user and admin login through login form, then will redirect to /admin
I have read the rails_admin wiki, but it seems just about configuration about single devise model, Can I define multi warden scope like following:
RailsAdmin.config do |config|
config.authenticate_with do
warden.authenticate! scope: [:user,:admin]
end
config.current_user_method(&:current_user)
config.current_admin_method(&:current_admin)
end
You can add more than one devise model. Here is an example (with a checksum authentication):
# initilizer/devise.rb
Devise.setup do |config|
config.warden do |manager|
manager.strategies.add :admin, Admin::ChecksumAuthenticatable
end
end
You class Admin::ChecksumAuthenticatable (for example) needs to inherit from ::Devise::Strategies::Base. Then define all methods you want and overwrite authenticate! method:
def authenticate!
admin = Admin.from_checksum_for_auth!(checksum)
# from_checksum_for_auth! is defined on Admin model and check checksum validity
success! admin
end
I am using devise gem in a rails application with multiple subdomains. Each subdomain is handled by respective controller, which look like this:
class Subdomain1Controller < ApplicationController
before_filter :authenticate_user!
def index
end
end
With above controller implementation, Devise always keep subdomain while redirecting user to login page. In above case, Devise redirect user to http://subdomain1.acmesite/users/sign_in instead of a common sign_in Url.
This leads to having multiple sign_in urls for each sub-domains.
http://subdomain1.acmesite/users/sign_in
http://subdomain2.acmesite/users/sign_in
http://subdomain3.acmesite/users/sign_in
I am wondering if it's possible to override devise method to exclude subdomain part from the url and yet keeping the previous page url information. More preciously, I wants Devise to redirect user to a specific Url (like: http://acmesite/users/sign_in) irrespective of subdomain and after successful authentication, Devise should return user back to the caller subdomain+page.
You need to write a custom FailureApp which kicks in when the user is unauthenticated.
From How To: Redirect to a specific page when the user can not be authenticated
class CustomFailure < Devise::FailureApp
def redirect_url
#return super unless [:worker, :employer, :user].include?(scope) #make it specific to a scope
new_user_session_url(:subdomain => 'secure')
end
# You need to override respond to eliminate recall
def respond
if http_auth?
http_auth
else
redirect
end
end
end
And add the following in config/initializers/devise.rb:
config.warden do |manager|
manager.failure_app = CustomFailure
end
If you’re getting an uninitialized constant CustomFailure error, and you’ve put the CustomFailure class under your /lib directory, make sure to autoload your lib files in your application.rb file, like below
config.autoload_paths += %W(#{config.root}/lib)
I droped Devise gem from my project and now using Sorcery instead.
Sorcery provide me complete control over controller and view, fully comply with my project requirements. With six months running on production after this transition, I am happy with Sorcery gem.
From reading the devise code and wiki it seems, there is no option to redirect user to registration page if a user is not logged in. In lib/devise/failure_app.rb, it appear that the redirect url is hardcoded.
def redirect_url
opts = {}
route = :"new_#{scope}_session_path"
opts[:format] = request_format unless skip_format?
if respond_to?(route)
send(route, opts)
else
root_path(opts)
end
end
I want to ask that's the best practice in getting the work done. I'm thinking of manually setting user_return_to session value, then make a call to registration page. Is that a good practice?
I think the best practice in this situation is following this devise wiki page: https://github.com/plataformatec/devise/wiki/How-To%3A-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
With the latest Devise, the recommendation is to follow these instructions:
https://github.com/heartcombo/devise/wiki/Redirect-to-new-registration-(sign-up)-path-if-unauthenticated
To do this, define a custom failure app with a route method that returns a symbol representing the named route to redirect to:
# app/lib/my_failure_app.rb
class MyFailureApp < Devise::FailureApp
def route(scope)
:new_user_registration_url
end
end
Then inside the Devise initializer, specify your failure app:
# config/initializers/devise.rb
config.warden do |manager|
manager.failure_app = MyFailureApp
end
Load the lib directory
config.autoload_paths << Rails.root.join('lib')