I have devise and CanCan set up, but I'm trying to get into sign up page in my rails application, but its redirecting me to home page saying that I'm not authorized to access this page.
I have a custom registration controller:
class Devise::RegistrationsController < DeviseController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
def edit
#user = User.find(current_user.id)
#profile = Profile.new
end
def update
# required for settings form to submit when password is left blank
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
protected
def after_update_path_for(resource)
user_path(resource)
end
private
end
It has something to do with before_filter :check_permissions,... because when I delete this line, I get an error saying
undefined method `user_registration_path'
from my registration form in my `devise/registrations#new:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
How do I fix my registration form?
Also, this is my routes:
devise_for :user, :controllers => { :registrations => "registrations" }, :skip => [:registrations, :sessions] do
get 'signup' => 'devise/registrations#new', :as => :new_user_registration
end
Okay from what it looks like you've done is used CanCans authorize! method to handle the authorization in a controller action which will raise the cancan exception error. Can you not try and do
check_authorization :unless => :devise_controller?
I back this up with a change made a cancan commit seen here adding :if and :unless options to check_authorization - closes #284. Comments can be seen in the following issue: GitHub issue 284. Where Ryan states:
It would be nice if the check_authorization method allowed a block to dynamically customize the behavior. If it returned true it would perform the authorization.
Correct me if I am wrong but is this not what you are trying to do. Hopefully this sheds some light.
Another thing with regards to your routes.rb where you have done:
devise_for :user, :controllers => { :registrations => "registrations" }, :skip => [:registrations, :sessions] do
get 'signup' => 'devise/registrations#new', :as => :new_user_registration
end
This all looks a bit mis-match to me. Do you have by any chance a devise folder containing all devise controllers in your controller directory. If so could you not try and make your devise route block look like the following:
devise_for :users, :controllers => {:sessions => 'devise/sessions', :registrations => 'devise/registrations', :passwords => 'devise/passwords'}, :skip => [:sessions] do
get '/login' => 'devise/sessions#new', :as => :new_user_session
get '/register' => 'devise/registrations#new', :as => :registrations
post '/login' => 'devise/sessions#create', :as => :user_session
get '/logout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
update
From the link GitHub - CheckAuthorizationI pasted in my comment I believe you can the method check_authorization as the example shows you can do:
class ApplicationController < ActionController::Base
check_authorization
end
The controller then shows that you can:
Supply the name of a controller method to be called. The authorization check only takes place if this returns false.
check_authorization :unless => :devise_controller?
Related
I am using rails 4 along with devise my routes.rb reads :
devise_for :users, :path => '',
:path_names => {:sign_in => 'login', :sign_out => 'logout', :sign_up => 'register'},
controllers: { registrations: "registrations", omniauth_callbacks: 'omniauth_callbacks' } do
get "/login" => "devise/sessions#new"
get "/signup" => "devise/registrations#new"
get "/logout" => "devise/sessions#destroy"
get "/login" => "devise/sessions#new"
end
resources :users, :only => [:show, :index], :path => "bloggers" do
resources :posts, :path => "articles"
end
Now when I create a new post as the currently signed in user (lets say the id is 1). The URL on the Post -> New action reads - > https://localhost:3000/bloggers/1/articles/new but I want to show
https://localhost:3000/articles/new, as the new action on the post should always be associated to the current_user.
I would imagine this being possible, but no clue how to do it.
Also a user has_many posts.
Help, please?
Authentication
Very simple actually:
#config/routes.rb
# If you have overridden "path_names", you don't need other paths for Devise
devise_for :users, :path => '',
:path_names => {:sign_in => 'login', :sign_out => 'logout', :sign_up => 'register'},
controllers: { registrations: "registrations", omniauth_callbacks: 'omniauth_callbacks' }
resources :posts, as: "articles", path: "articles"
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!, except: :index
def index
#Stuff here
end
end
The bottom line here is that whenever you want to use an authenticated area (especially with Devise), you just need to "protect" the controller#actions which you want to limit access to. The beauty is that you can use the authenticate_user! helper method to make these work
Further to that, you will then be able to just call current_user in your controller (not having to set the user, as you're doing now):
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = current_user.posts.new
end
def create
#post = current_user.posts.new post_params
#post.save
end
private
def post_params
params.require(:post).permit(:x, :y, :z)
end
end
I am using devise and want to specify different redirect after updating a user based on a conditional statement. I did follow this https://github.com/plataformatec/devise/wiki/How-To:-Customize-the-redirect-after-a-user-edits-their-profile and it is not calling my custom after_update_path_for method.
routes.rb
devise_for :users, :skip => [:registrations], :controllers => { :registrations => :registrations }
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users/:id' => 'devise/registrations#update', :as => 'user_registration'
end
The reason I have the skip registrations is because I do not wish to have the new and create routes for the user. I am not sure if the issue is with the routes or something else.
Here is the registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
protected
def after_update_path_for(resource)
binding.pry
if current_user.position == "owner" && current_user.sign_in_count == 1
hub_landing_path
end
end
end
Any help is greatly appreciated. This is really stumping me so if anyone has any ideas I would love to try them out.
You need to change
put 'users/:id' => 'devise/registrations#update', :as => 'user_registration'
to use your custom RegistrationsController controller. So it'll be:
put 'users/:id' => 'registrations#update', :as => 'user_registration'
The accepted answer by #NARKOZ does not work for me for Rails 4. I had to override the Devise Passwords controller like this:
# users/passwords_controller.rb
class Users::PasswordsController < Devise::PasswordsController
protected
def after_resetting_password_path_for(resource)
signed_in_root_path(resource)
end
end
There is documentation on this method here: https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in
Default Devise user signup form looks like this:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
When I run rake routes I don't see any registration prefix, there is user_registration, new_user_registration etc. but not just registration, so how does it work? Where can I find it's source code?
The registration_path route is generated in
Devise::Controllers::UrlHelpers.
As you can see in the code below it just calls your regular routes.
def self.generate_helpers!(routes=nil)
routes ||= begin
mappings = Devise.mappings.values.map(&:used_helpers).flatten.uniq
Devise::URL_HELPERS.slice(*mappings)
end
routes.each do |module_name, actions|
[:path, :url].each do |path_or_url|
actions.each do |action|
action = action ? "#{action}_" : ""
method = "#{action}#{module_name}_#{path_or_url}"
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
def #{method}(resource_or_scope, *args)
scope = Devise::Mapping.find_scope!(resource_or_scope)
_devise_route_context.send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
end
URL_HELPERS
end
end
end
end
generate_helpers!(Devise::URL_HELPERS)
With Devise::URL_HELPERS being:
{:session=>[nil, :new, :destroy], :omniauth_callback=>[], :password=>[nil, :new, :edit], :registration=>[nil, :new, :edit, :cancel], :confirmation=>[nil, :new], :unlock=>[nil, :new]}
Put the following in your routes.rb
devise_for :users, :controllers => {:sessions => 'devise/sessions', :registrations => 'devise/registrations',
:passwords => 'devise/passwords'}, :skip => [:sessions] do
get '/login' => 'devise/sessions#new', :as => :new_user_session
post '/login' => 'devise/sessions#create', :as => :user_session
get '/logout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
And then run rake routes. You can find the devise controller at: List of Devise Controllers
I have this in my routes file:
devise_for :users, :controllers => { :registrations => "users/registrations",
:sessions => "users/sessions",
:omniauth_callbacks => "users/omniauth_callbacks" }
devise_scope :user do
get 'sign_in', :to => 'users/sessions#new', :as => :new_user_session
get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
end
and now I added:
resources :users
because I want an admin user to be able to see all the users.
In CanCan I have this:
class Ability
include CanCan::Ability
def initialize(user)
if user
if user.admin?
can :manage, :all
end
can [:read, :edit, :update], User, :id => user.id
end
can [:create], User
end
end
Is this secure enough? Should I also add a before_filter in the UserController to block calls to the newly created route? Something like this? Problem with this is that users can't change their own profile anymore...
before_filter :check_rights
private
def check_rights
unless current_user.admin
redirect_to root_path
end
end
Found the solution. The before_filter is completely correct.
I had added
resources :users
before the devise part, you need to put it behind that part then users can still alter their own profile but can't go to index for example.
Referenced the online documents for Devise but my override is still not working. Any one got any suggestions why not? It just goes to the root after sign in. Sign up works though.
Routes:
root :to => 'pages#index'
get "pages/index"
devise_for :users, :path => 'accounts', :controllers => { :registrations => "registrations" }
match 'profile' => 'profiles#show', :as => 'current_profile'
match 'profile/edit' => 'profiles#edit', :as => 'edit_current_profile'
put 'profile' => 'profiles#update'
resources :users do
resources :profiles
end
Registration Controller:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
edit_current_profile_path
end
def after_sign_in_path_for(resource)
current_profile_path
end
end
def after_sign_in_path_for(resource)
current_profile_path
end
This goes in the application_controller, not the override class.