I'm have some issues with devise. I've added a users table (devise), an appointments table and a profiles table to my app. One user should have many appointments, and have one profile.
The problem is that I can't display the profile for that user. It tells me that there is No route matches [GET] "/profile/1"
I wonder if anyone can point out where I'm going wrong, and why?
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :profiles
resources :appointments
root 'page#home'
get 'page/testimonials'
get '/signedinuserprofile' => 'profiles#signedinuserprofile'
#get 'page/home'
profiles controller:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#profiles = Profile.all
end
def signedinuserprofile
profile = Profile.find_by_user_id(current_user.id)
if profile.nil?
redirect_to "/profile/new"
else
#profile = Profile.find_by_user_id(current_user.id)
redirect_to "/profile/#{#profile.id}"
end
end
application controller:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
"/signedinuserprofile"
end
end
sessions controller:
class SessionsController < Devise::SessionsController
#after_sign_in_path_for is called by devise
def after_sign_in_path_for(user)
"/signedinuserprofile" # here I provide the path for the user's profile
end
end
You're trying to use a resourceful route yet you don't have a show action in your controller. Add a show action and find the user from the params #profile.id
You are essentially redirecting to an action that you don't have in your profiles controller.
Related
I want users to access the home page whether they're authenticated or not. Though, whenever the root url is visited, the user is immediately redirected to /users/sign_in. How do I disable this?
routes.rb
Rails.application.routes.draw do
root 'home#index'
devise_for :users
end
home_controller.rb
class HomeController < ApplicationController
before_action :authenticate_user!, except: [:index]
def index
end
end
With Devise is usually better to require authentication by default and then add exceptions with skip_before_action:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
end
class HomeController < ApplicationController
skip_before_action :authenticate_user!
def index
end
end
This avoids the risk of leaving security holes just by programmer omission.
I'm new to pundit and trying to come up with the best approach for handling nested resources for the index action. I found a similar question however it doesn't deal with admin privileges and I'm just not sure if my solution feels quite right.
Let's say I have two models, a User can have many notes and a Note which belongs to a single user. Users cannot look at notes from other users unless they're an admin. At the same time, admin's are able to create their own notes and therefore must also have the ability to retrieve a list of them via their own index action.
routes.rb
resources :users, only: :show do
resources :notes
end
notes_controller.rb
class NotesController < ApplicationController
#would probably move to application_controller.rb
after_action :verify_authorized
after_action :verify_policy_scoped
def index
user = User.find(params[:user_id])
#notes = policy_scope(user.notes)
authorize user
end
#additional code
end
note_policy.rb
class NotePolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin? && scope != user.notes
scope
else
user.notes
end
end
end
#additional code
end
user_policy.rb
class UserPolicy < ApplicationPolicy
def index?
user == record || user.admin?
end
#additional code
end
You are overthinking it:
class NotePolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(user: user)
end
end
def index?
record == user || user.admin?
end
# ...
end
Note here that its a good idea to chain from the scope being passed in from policy_scope. It lets your controller set up any scopes unrelated to authorization like for example pagination.
Also in index? we are cheating slightly. Instead of passing a note instance we are just passing the user.
class NotesController < ApplicationController
before_action :set_user!, only: [:index] # ...
before_action :set_note!, only: [:show, :edit, :update, :destroy]
def index
#notes = policy_scope(Note.all)
authorize(#user)
end
# ...
private
def set_user!
#user = User.find(params[:user_id])
end
def set_note!
#note = authorize( Note.find(params[:id]) )
end
end
Using before_action in this way is a pretty good pattern as it sets up all the "member" actions for authorization.
I'm having trouble with configuring the model which is generated by scaffold in my Rails 4 application.
These are my models:
class Contact < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :contact
after_create :make_contact
def make_contact
create_contact(
:country => "USA",
:city => "Newyork"
)
end
end
As you can see, I'm creating a Profile model instance for each User when they registered for the site.
I generated User model with Devise GEM and generated Contact model with rails scaffold generator.
1) I want my Users to only update or view their profile. I want to prevent them to list all profiles, destroy their profiles or create a new profile. What is the best approach to do this?
2) I want my application to redirect automatically to the users related profile page when they visit /contacts route.
3) User can't be able to see other users profiles by changing the URL like /contacts/1, contacts/2 etc.
How can I do this?
Thanks.
User before_filter/before_action in your controller
def UsersController < ApplicationController
before_filter :restrict_user, :only => [:show, :edit, :update]
private
def restrict_user
redirect_to :root, :alert => "Not authorized" unless params[:id] = current_user.id
end
end
In your routes, you can specify only the actions that you want
resources :users, :only => [:new, :create, :edit, :update, :show] #index and destroy are not in the list
You can do the same in contacts controller too
I'm using Devise as authenticating solution in Rails and I have a cached fragment :recent_users.
I want this fragment to expire when a new user is registered, changed or removed, so I put in my(manually created) users_controller.rb
class UsersController < ApplicationController
cache_sweeper :user_sweeper, :only => [:create, :update, :destroy]
...
But my fragment does not expire when new creates or changes.
My user_sweeper contains basic prescriptions
class UserSweeper < ActionController::Caching::Sweeper
observe User
def after_save(user)
expire_cache(user)
end
def after_destroy(user)
expire_cache(user)
end
private
def expire_cache(user)
expire_fragment :recent_users
end
end
What am I doing wrong?
Problem solved!
I followed this steps and everything works:
$ mkdir app/controllers/users
$ touch app/controllers/users/registrations_controller.rb
In registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
cache_sweeper :user_sweeper, :only => [:create, :update, :destroy]
end
Problem was that Registrations in Devise is a separate controller.
Put this in applications_controller.rb
class ApplicationController < ActionController::Base
cache_sweeper :user_sweeper, :only => [:create, :update, :destroy]
...
I have a number of resources (Trips, Schedules, etc) with actions that should be limited to just the resource's owner.
How do you implement code with a #require_owner method defined in ApplicationController to achieve this? Ideally, the code will look up the inheritance chain for the owner so the before_filter will work on a :comment that belongs_to :trip that belongs_to :user.
class TripsController < ApplicationController
belongs_to :member
before_filter :require_owner
...
end
I don't fully follow the description (would a comment really be owned by the trip owner?), but expanding slightly on jonnii's answer, here is an example that restricts the trip controller:
class ApplicationController < ActionController::Base
...
protected
# relies on the presence of an instance variable named after the controller
def require_owner
object = instance_variable_get("##{self.controller_name.singularize}")
unless current_user && object.is_owned_by?(current_user)
resond_to do |format|
format.html { render :text => "Not Allowed", :status => :forbidden }
end
end
end
end
class TripsController < ApplicationController
before_filter :login_required # using restful_authentication, for example
# only require these filters for actions that act on single resources
before_filter :get_trip, :only => [:show, :edit, :update, :destroy]
before_filter :require_owner, :only => [:show, :edit, :update, :destroy]
...
protected
def get_trip
#trip = Trip.find(params[:id])
end
end
Assuming the model looks like this:
class Trip < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
...
def is_owned_by?(agent)
self.owner == agent
# or, if you can safely assume the agent is always a User, you can
# avoid the additional user query:
# self.owner_id == agent.id
end
end
The login_required method (provided by or relying on an auth plugin like restful_authentication or authlogic) makes sure that the user is logged in and provides the user with a current_user method, get_trip sets the trip instance variable which is then checked in require_owner.
This same pattern can be adapted to just about any other resource, provided the model has implemented the is_owned_by? method. If you are trying to check it when the resource is a comment, then you'd be in the CommentsController:
class CommentsController < ApplicationController
before_filter :login_required # using restful_authentication, for example
before_filter :get_comment, :only => [:show, :edit, :update, :destroy]
before_filter :require_owner, :only => [:show, :edit, :update, :destroy]
...
protected
def get_comment
#comment = Comment.find(params[:id])
end
end
with a Comment model that looks like:
class Comment < ActiveRecord::Base
belongs_to :trip
# either
# delegate :is_owned_by?, :to => :trip
# or the long way:
def is_owned_by?(agent)
self.trip.is_owned_by?(agent)
end
end
Make sure to check the logs as you are doing this since association-dependent checks can balloon into a lot of queries if you aren't careful.
There's a few different ways to do this. You should definitely check out the acl9 plugin (https://github.com/be9/acl9/wiki/tutorial:-securing-a-controller).
If you decide you want to do this yourself, I'd suggest doing something like:
class Trip < ...
def owned_by?(user)
self.user == user
end
end
class Comment < ...
delegate :owned_by?, :to => :trip
end
# in your comment controller, for example
before_filter :find_comment
before_filter :require_owner
def require_owner
redirect_unless_owner_of(#commemt)
end
# in your application controller
def redirect_unless_owner_of(model)
redirect_to root_url unless model.owned_by?(current_user)
end
Forgive me if there are any syntax errors =) I hope this helps!
Acl9 is a authorization plugin. I'd give you the link, but I don't have cut and paste on my iPhone. If no one else provides the link by the time I get to a computer, I'll get it for you. Or you can google. Whichever. :)
I have only just started using it, but it has an extremely simple interface. You just have to create a roles table and a roles_user. Let me know how it goes if you decide to use it.
Or just use inherited resources:
InheritedResources also introduces another method called begin_of_association_chain. It’s mostly used when you want to create resources based on the #current_user and you have urls like “account/projects”. In such cases you have to do #current_user.projects.find or #current_user.projects.build in your actions.
You can deal with it just by doing:
class ProjectsController < InheritedResources::Base
protected
def begin_of_association_chain
#current_user
end
end