RESTful account selection controller in Rails? - ruby-on-rails

In my app, after a user logs in, he is redirected to the AccountsSelection controller. In there, I have an index action that basically will get all the potential accounts a user can use, and display them in a index view.
def index
#accounts = current_user.eligible_accounts
end
In that page the user can click in one of the accounts, and that should go to one of the actions in the controller, that will set this in the session:
def show
session[:selected_account] = params[:account_id]
redirect_to account_path
end
Right now I have made that action to be show (so the selection of the account is doing a GET request, but I am not sure if this is a RESTful way of approaching this). From my point of view this is not a PUT/POST because I am not changing any resource or creating any resource, but it seems weird to be using the show action to just set a session.
What would be a proper way of handling this? It is correct to use show?

It is not recommended to use session in this case. So the standard approach is to create a before_action to set the account_id.
Use like this:
before_action :set_account, only: [:show]
and create the function in private as:
private
def set_account
account_id = params[:account_id]
end
Hope this helps.

Related

Using devise for user registration/login redirect user to a different form page based on their response

I am using devise for user registration/login, after the user has successfully signed up, I want to show a page/a dialog box and redirect to another page based on user response. How can I do that?
User Model (By devise)
username
password
Student Model
name
student_id
Teacher Model
name
grade
First_page:
signup signin links
Signup link will show the devise views/devise/registrations/new.html.erb page.
After successful signup, it takes the user to root page. I have defined the root page in routes.rb:
` Rails.application.routes.draw do
devise_for :users
resources :students, :teachers
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root to: "students#index"
end `
At this point, the application doesn't have any idea who the user is.
So, I want to get the identity information(student/teacher) from the user.
How will I get this information?
Student/Teacher controller:
`class StudentsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
def index
#students = Student.all
end
def new
#student = Student.new
end
def create
current_user.create_student(student_params)
redirect_to root_path
end
private
def student_params
params.require(:student).permit(:name, :skypid)
end
end`
After the user has successfully signed in, I want to ask if the user is a student or teacher. Based on what they select, redirect them to a student form page or teacher form page.
How can I do that in rails?
Thank you
You can write a custom after_sign_in_path_for function in your ApplicationController assuming you're using all the default Devise controllers otherwise. Any named path helper or other route that it returns will be where the user is redirected, so you could do something simple like always redirect to a selection page that presents the options and handles the choice on a subsequent action:
def after_sign_in_path_for(resource)
user_type_selection_path # whatever route in your app manages the selection
end
Alternately, you could invoke a custom method on the user model in that function to make a choice right there:
def after_sign_in_path_for(resource)
resource.student? ? student_path : teacher_path
end
You could hybridize these of course as well to do the latter when the selection has already been made and redirect otherwise, with something similar to the following:
def after_sign_in_path_for(resource)
if resource.user_type_chosen?
resource.student? ? student_path : teacher_path
else
user_type_selection_path
end
Bear in mind that none of those functions or paths are real, since I can't be more specific on the information you've provided, but hopefully this gets you headed in the right direction. The after_sign_in_path_for hook is your primary tool here, unless you get into the world of overriding the default devise controllers and interrupting the usual workflow there to accommodate this step, which doesn't seem strictly necessary by your description.

Redirect User to Root if not admin

I'm currently trying to make it so that if a user is not an admin they cannot get onto the 'create & edit' pages and also cannot destroy entries.
I have this method within my controllers
def must_be_admin
unless current_user && current_user.admin?
redirect_to root_path, notice: "Admin Needed."
end
end
and I call it like so:
before_filter :must_be_admin, only: [:edit, :destroy, :create]
This seems to let unlogged in users on the create page, but doesn't let them do the actual create action. Is there anyway to NOT allow users on the actual pages as well?
Essentially don't allow any users that aren't admin to
create/edit/destroy (as well as not let them on the actual pages) and
just reroute them back to index.
Yes if you restrict the new and edit actions as well they will not be able to see any of the pages. If you have a standard CRUD controller just eliminate the "only" option in your before_action and all non-admins will be blocked.
You have to remember that while blocking the create action you are not blocking the "new" actions page displaying everything. Blocking the 'new' action as well will stop them from even seeing the page.

Rails route error: NoMethodError

I'm new to Rails, and am trying to create a page that is largely a copy of my users#show page, with slightly different content and formatting. Ideally, this would work something like this:
Normal route: http://myUrl.com/users/2
New route: http://myUrl.com/users/2/lightbox <-this is the new route with the formatting. It should have access to user #2's info.
I did some research on stack overflow, and added the following to routes.rb
resources :users do
member do
get 'lightbox'
end
end
and then raked the routes. This allows me to type in the url http://myUrl.com/users/2/lightbox . However, it doesn't seem to have access to any of the user class's resources, and seems to have no idea who User #2 is.
I may completely have gone about this the wrong way - all I really want to do is create a custom page to display an individual user's information that's different from the show page. I'd really appreciate any help!
You need to add an action to your app/controllers/users_controller.rb:
def lightbox
#user = User.find(params[:id]
# any other logic, look at your show method
end
Routing only maps a url to a controller action. It is up to the controller action, each individually, to set variables and render the view.
Before filters and helper methods are used make sure you don't have to write code a bunch of times. For example:
before_filter :find_user, only: [ :show, :lightbox ]
def show
end
def lightbox
end
protected
def find_user
#user = User.find(params.fetch :id)
end

Rails 3, how to secure and protect controllers and urls

I'm sort of new to rails, what I want to to is protect users profile
what I mean is if user 1 login and go to edit his profile he can, but also if he change on the url to user to # 2 they can also change their information
localhost:3000/users/2/edit
I'm a little lost, any help will be greatly appreciated, or suggestions of good books/blogs
As part of authentication add a session variable, session[:user_id] = User.Authenticate(params[:user][:username], params[:user][:password) (this is the general pattern, you need to add another resource for authentication).
Then add a before_filter function to your controller and check if session[:user_id] == params[:id]. Look at it here: before_filter
The Rails Security Guide is probably a good place to start
Just in case this is useful to someone, something that I came across when testing my app was although users that hadn't signed in couldn't access restricted content, once a user was signed in, they could change the url to a another users id, eg.
/users/3 and it would then show that users home page. So any user could look at any other user, which wasn't what I wanted.
To get around this, I changed the user controller to secure against this:
class UsersController < ApplicationController
#first call the correct_user function before allowing the show action to proceed
before_filter :correct_user, only: [:show]
...
def show
#do whatever here
end
...
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end

Best way to restrict actions (edit/delete) with Ruby on Rails and Devise

I am fairly new to Ruby On Rails and right now I am doing a simple app. In this app a user can create many items and I use devise for authentication. Ofcourse I want to make sure that you are the owner in order to delete items (Teams, Players etc) and the way I do it now is:
def destroy
#team = Team.find(params[:id])
if current_user.id == #team.user_id
#team.destroy
redirect_to(teams_url, :notice => 'The team was deleted.')
else
redirect_to root_path
end
end
Is this the best way? I was thinking about putting a method in the model but I am not sure I can access current_user from there. I was also thinking about a before_filer, something like:
before_filter :check_ownership, :only => [:destroy, :update]
I that case and if I want to code only one method for all objects (all objects this relates to have a "user_id"-field)
In my application controller I put:
before_filter :authorize
def authorize
false # Or use code here to check if user is admin or not
end
Then I override the authorize method in my actual controller to allow access to various actions.
You're looking for an authorization solution on top of your authentication (devise)
You can't access current user in the model no. I've had a fair amount of success using Makandra's Aegis for authorization. It allows you to create roles specify permissions attributed to each role. The docs are pretty good and I know it works fine with Devise, it's pretty agnostic that way, I've also used it with Clearance. It also passes an implicit "current_user" to your permissions so you can specify and check that your current user can take appropriate actions.

Resources