How to use both Omniauth_callbacks controller and custom devise registrations controller - ruby-on-rails-3

I have a custom registrations controller for devise set up, which is this:
devise_for :users, controllers: {registrations: "registrations"}
and in the controller:
class RegistrationsController < Devise::RegistrationsController
protected
def after_update_path_for(resource)
user_path(resource)
end
end
It works great.
However I also have omniauth authentication, which again works great...by itself:
devise_for :users, controllers: {omniauth_callbacks: "omniauth_callbacks"}
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to sign_up_path
end
end
alias_method :linkedin, :all
alias_method :twitter, :all
end
However as you can probably already see my problem - I'm not sure how to get them to work together, as they both start with 'devise_for :users' and so whichever way round I place them in the routes file, one won't work.
How can I get them both working at the same time, so that the registrations controller only overrides the 'edit' and 'update' actions, while the omniauth_callbacks controller handles authentication?
Thanks

In routes.rb, you can put comma seperated paths for devise_for like this -
devise_for :users, controllers: {registrations: "registrations", omniauth_callbacks: "omniauth_callbacks"}
This will work.

Related

How to create path for users and admin after signed in rails 5

I have a created a rails 5 app using devise gem that has both a user and admin sign in. They are separate modals and have found in my lessons and created the logic for the admin to be directed to the dashboard after signing in however what is the best method to direct the user to the home page after signing in. Below is my set up for the admin and it works for the admin however when I sign in as the user they are directed to the dashboard as well. Outcome I need to see is the user goes to home page after signing in and admin goes to dashboard.
routes:
root 'pages#home'
devise_for :users,
path: '',
path_names: {sign_in: 'user_login', sign_out:
'user_logout', edit: 'user_profile', sign_up: 'user_registration'},
controllers: { omniauth_callbacks:
'users/omniauth_callbacks', registrations: 'registrations' }
devise_for :admins,
path:'',
path_names: {sign_in: 'login', sign_out:
'logout', edit: 'profile', sign_up: 'registration'},
controllers: { omniauth_callbacks:
'admins/omniauth_callbacks', registrations: 'registrations' }
resources :admins, only: [:show]
resources :photos
resources :locations
resources :deals
get 'dashboard' => 'admin_home#dashboard'
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if:
:devise_controller?
protected
def after_sign_in_path_for(_resource_or_scope)
dashboard_path
end
You can use devise methods for this purpose where you can define paths based on some custom checks i.e., user role.
class ApplicationController < ActionController::Base
protect_from_forgery
protected
def after_sign_in_path_for(resource)
sign_in_url = new_user_session_url
if request.referer == sign_in_url
super
else
stored_location_for(resource) || request.referer || root_path
end
end
end
Reference: https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in

How can I default an ancestral relationship with cancan to an internal node of the tree?

I am using cancan to authorize my controller actions. One of classes where access is authorized by cancan is a tree, implemented with acts_as_ancestry. I'm having problems using load_and_authorize_resource when the user is not permitted to access the root level, but rather is allowed access starting at an interior node.
Here are some relavant class definitions:
class User < ActiveRecord::Base
belongs_to :organization, :inverse_of => :users
end
class Post < ActiveRecord::Base
belongs_to :organization, :inverse_of => :posts
end
class Organization < ActiveRecord::Base
has_ancestry :cache_depth => true
has_many :users, :inverse_of => :organization
has_many :posts, :inverse_of => :organization
end
The rules for managing posts are "You can manage posts in any organization below yours". My cancan abilities definition is this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
# subtree_ids is added by acts_as_ancestry
can :manage, Post, {:organization_id => user.organization.subtree_ids}
end
end
In the controller, I have this (other actions omitted)
class PostsController < ApplicationController
load_and_authorize_resource :post
def index
end
def new
end
end
Everything works fine when the authorized user belongs to the root organization. However, when I login as a user authorized at an internal node, the index action works fine, but when the new action is invoked, I get a can-can authorization error.
Here is what I see in the log:
Access denied on new #<Post id: nil, organization_id: 1>
The organization_id 1 (the root) is coming from the schema:
create_table "posts", :force => true do |t|
t.integer "organization_id", :default => 1
end
With cancan, the new action will build a new Post and assign it to #post. When it does this, it will initialize all the attributes with values taken from the can definition in Abilities.rb. However, it will not do anything if those attributes are Arrays, Hashes or Ranges and the default value ends up coming from the schema.
How can I authorize users to manage posts in their subtree, but when they create a new post, default it to their organization?
In cancan, if the #post variable is already initialized by you, it will not call load_resource on it, only do the authorize part. See this part of the docs: https://github.com/ryanb/cancan/wiki/Authorizing-controller-actions, "Override loading".
So the simplest solution is to take control of the initialization yourself and make it what you need, like here:
class PostsController < ApplicationController
before_filter :initialize_post, :only => [:new, :create]
def initialize_post
#post = current_user.organization.posts.build(params[:post]||{:name=>'Smashing Kittens'})
end
load_and_authorize_resource :post
def index
end
def new
end
def create
end
end
You can see it working in this test project that I created from your post: https://github.com/robmathews/cancan_test.
I had a similar issue and ended up writing ancestry related permissions in blocks like so:
can :manage, Post do |post|
post.organization.subtree_ids.include?(user.organization_id)
end

Devise redirect after signin with username in url

I'm struggling with getting devise to redirect to a user's profile page after signin. My routes file looks like this:
get "profiles/index"
get "users/index"
get "users/show"
authenticated :user do
root :to => 'home#index'
end
root :to => "home#index"
devise_for :users
resources :users
scope ":username", :as => "user" do
match '/', :to => 'profiles#index'
end
I would like it to redirect to /myusername which the user's profile page. Thanks for your guys' help.
If you want to change the sign in redirect you can override the after_sign_in_path_for method by adding a new SessionsController as so:
class SessionsController < Devise::SessionsController
#after_sign_in_path_for is called by devise
def after_sign_in_path_for(user)
"/users/#{user.username}" #adjust the returned path as needed
end
end
As Ashikata mentioned you need to change the devise routing to the following if you're changing the session controller.
devise_for :users, :controllers => { :sessions => 'sessions' }
Alternatively, adding that modified after_sign_in_path_for method to your application controller should do the trick.

rails 3.1, why I can't override 'create' at Devise::SessionsController?

I have devise 1.4.8, and I am trying to override create at Devise::SessionsController to execute some logic once user signed in.
Here is my class: (stored at \app\controllers\students\sessions_controller.rb)
class Students::SessionsController < Devise::SessionsController
def create
super
end
end
here is my routes.rb
devise_for :students, :controllers => { :sessions => "students/sessions" }
but, overridden create is never called! instead, only the create at super class is called
any idea ?
it was a problem in the routes.rb,
I have this line, and it worked:
devise_for :students do post '/students/sign_in' => 'students/sessions#create' end
but, I've deleted all lines for :students at routes.rb

Ruby on Rails nested resource routing error

I have a user, comment, and route model as shown:
class User < ActiveRecord::Base
has_many :routes, :dependent => :destroy
has_many :comments, :dependent => :destroy
end
class Route < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
end
I have the routes.rb file nesting comments and routes within user as shown:
MyApp::Application.routes.draw do
resources :users do
resources :comments
resources :routes
end
When I run 'rake routes', the route to the Routes_controller index appears as so:
user_routes GET /users/:user_id/routes(.:format) {:action =>"index", :controller=>"routes"}
Yet for some reason when a user is signing in, I get a routing error saying that the routes controller cannot be found. This happens when the system is posting a new session in the session controller. I know that it attempts to sign in the user, but fails on the redirect. Any suggestions?
class SessionsController < ApplicationController
...
def create
user = User.authenticate(params[:session][:email],
params[:session][:password])
if user.nil?
flash.now[:error] = "Invalid email/password combination."
#title = "Sign in"
render 'new'
else
sign_in user
redirect_to user_routes_path
end
end
...
end
For some reason, the stack trace wasn't displayed when I redirect to user_routes_path, so I have it direct to root_path and the same thing happens. Here is the trace for that:
app/views/layouts/_header.html.erb:3:in
`_app_views_layouts__header_html_erb___917786942_46449696_315190'
app/views/layouts/application.html.erb:11:in
`_app_views_layouts_application_html_erb__423035099_46500948_0'
I will give it a try, after reading Fernandez: The rails 3 way about redirect_to.
When you look at the output from rake routes, you have the output:
user_routes GET /users/:user_id/routes(.:format) {:action =>"index", :controller=>"routes"}
The methods you may use to that route are:
user_routes_url: Full URL (with protocol and everything)
user_routes_path: Relative URL to the host
But your user_routes tells you another thing: the URL has to contain a user_id, and this user_id has to come from somewhere. So to call the different url and path methods, you have to look at the arguments:
users_path: no argument, shows all users
user_path(#user): one argument, because the information about the user is needed. Could be the user, or the user_id
`user_routes_path(#user): needs the user, so that all routes (index view) for one user could be shown.
So include in you source code in the controller:
...
else
sign_in user
redirect_to user_routes_path(user)
end
...
I don't understand the error message you have appended, but I think you should first correct the path call.