I'm trying to set up something that allows users to go to certain urls only under certain circumstances. Right now I have a setEvent/:id url that sets a property on users to an event_id, then redirects the user to the event url. The user can access a url like .../whatever/event/1 where 1 needs to equal the event_id, and if it doesn't it redirects the user.
However, this doesn't stop someone from just typing .../whatever/setEvent/:id into their address bar to get access to the page.
The proper way to do this is with a before action in your controllers. Here is an example from one of my apps where a user who is not logged in will always be redirected to the new_session URL.
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?, :herd_user
def herd_user
redirect_to new_session_url unless logged_in?
end
... other medthods...
end
and
class StaticPagesController < ApplicationController
before_action :herd_user
def index
end
end
without bringing in more gems you can just do a before_action
before_action :enforce_tenancy, except: [:index]
before_action :allow_only_admin, only: [:index]
private
def enforce_tenancy
render unauthorized unless event.user_id == current_user.id
end
def allow_only_admin
render no_way_sucka_path unless current_user.admin?
end
I had a similar problem and this might not be the best way to handle it but in the action for that page you can check the current url and check the property then redirect to the one they can access if they go to an incorrect url.
Something kind of like:
url_id = request.fullpath.sub('/whatever/event/', '')
redirect_to user_page_path(user.id) unless (current_user.event_id.to_s == url_id)
Sorry if the code isn't great I tried to write it based off of the info you gave.
Edit* Make sure to do this before getting any info for the page from your database or it will be less efficient.
Related
I want one pages of my ruby on rails web application inaccessible to one of my STI model types. I have two models typeA and typeB inheriting from User. I have used the column type in the User table to implement STI. I am using Devise gem for User sessions. I want one webpage 'http://localhost:3000/rate' inaccessible to my typeA User. Whenever an User logs in who is of the type 'typeA', he does not have the option of seeing the link 'Rate'. But I also do not want him to be able to access that page by the link 'http://localhost:3000/rate'. If he tries to access it through that link, I want to sign him out and make him log in again.
I managed this by using a piece of code in my Controller with the specific method for 'rate'.
def rate
if current_user.type == "typeA"
sign_out(current_user)
redirect_to new_user_session_path
else
#Code for User of typeB
end
end
This is working but I wanted to know if this can be done in a better way using before_filter :authenticate_user! or something else
Right now my before_filter part looks like this
before_filter :authenticate_user!, except: [:index, :show]
Is there any way I can make a change to the upper code to achieve that functionality.
P.S: Maybe this can be done better if I had used roles or other gems like CanCan/Pundit but I do not have much time left to submit my project, so I do not want to get into all that right now.
you can add another before_filter on the controller you want to restrict the access just to confirm your STI user type without overiding devise's authenticate_user! filter.
application_controller.rb
class ApplicationController < ActionController::Base
def confirm_user_type(user_type)
redirect_to new_user_session_path unless current_user.is_a?(user_type)
end
end
pages_controller.rb
class PagesController < ApplicationController
# must be authenticated to access
before_filter :authenticate_user!
# must be user of TypeA to access
before_filter { |c| c.confirm_user_type(TypeA) }
def rate
...
end
end
Then, you can use the same filter before_filter { |c| c.confirm_user_type(TypeB) } for STI user type: 'TypeB'
Try this:
class ApplicationController
before_action :authenticate_user!
def authorize_user!
if current_user.type == "typeA"
sign_out(current_user)
redirect_to new_user_session_path
end
end
end
with your controller:
class SomeController < ApplicationController
before_action :authorize_user!, except: [:index, :show]
def top_secret
...
end
end
I believe if a before_action (the new name for before_filter) renders or redirects, the action won't be processed.
I have a Ruby on Rails web application, where a user needs to provide his nickname and password to register. After successful registration, the user is redirected to an edit page, where he should enter his email.
The use case requires to always redirect the user to the edit page until he has submitted a valid email address. However, in the current state, the user can click on any menu item and is then taken to the corresponding page. How do I prevent this behavior and redirect the user back to the edit page when clicking on a menu link?
You can create a before_action in your ApplicationController which checks if the user is logged in and has submitted his email, something like this:
class ApplicationController < ActionController::Base
before_action :validate_email_confirmed
def validate_email_confirmed
return unless current_user
redirect_to user_edit_path unless current_user.email?
end
end
Keep in mind that you have to skip this before_action for both the user edit and update actions, or you'll end up having a redirect loop. This is how to skip an existing before_action for specific actions:
class UsersController < ApplicationController
skip_before_action :validate_email_confirmed, only: [:edit, :update]
end
Did some more digging, and found this on the official docs, seems to fit your needs:
class ApplicationController < ActionController::Base
before_action LoginFilter
end
class LoginFilter
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in to access this section"
controller.redirect_to controller.new_login_url
end
end
end
You'd of course have to rework this some, to get awaiting_email or such, but the principle is the same.
Can anyone give any pointers to how we can customise Devise so that if an unauthenticated user visits the root_url, they are redirected to /users/sign_in but not shown the "You need to sign in or sign up before continuing." error message?
To expand further: we want that message to be shown if the user tries to visit a 'deeper' URL (/controller/restricted_action) directly without first authenticating, but for someone with the app bookmarked as www.app.com we just want to redirect them straight to the login page without any error being shown.
Had a look around through the SessionsController (to ascertain whether a custom controller could work) and various other places, but can't see a place or how we can override the behaviour in this way.
Any advice appreciated!
This should remove the flash alert message if redirected from "/" to "/users/sign_in".
class ApplicationController < ActionController::Base
before_action :keep_previous_url
before_action :no_unauthenticated_alert_if_redirected_from_root
def keep_previous_url
session[:previous_url] = request.fullpath
end
def no_unauthenticated_alert_if_redirected_from_root
if request.fullpath == "/users/sign_in" && session[:previous_url] == "/"
flash.now.alert = nil
end
end
end
#Anthony's suggestion above is good. Although I think we can improve on it slightly. With this below solution, there is only 1 before_action being called in the ApplicationController (the less the better since it's used by all), and then one in your root or home controller itself.
Also, it won't keep storing the previous_url unnecessarily in the user session, saving memory.
Add this to your ApplicationController:
class ApplicationController < ActionController::Base
before_action :_no_unauthenticated_alert_if_redirected_from_root
private
def _no_unauthenticated_alert_if_redirected_from_root
return if session[:requiring_sign_in_from_root] != true
session[:requiring_sign_in_from_root] = nil
flash.clear
end
end
and add this to the controller that serves your root "/" url:
class HomeController < ApplicationController
prepend_before_action :_check_if_requiring_sign_in_from_root
private
def _check_if_requiring_sign_in_from_root
session[:requiring_sign_in_from_root] = true unless user_signed_in?
end
end
The methods don't need to be private, but I think it's good practice.
I have fixed this by doing the following:
authenticated :user do
root to: 'home#index', as: :home
end
root to: redirect('/users/sign_in')
Now I don't the error message when going to the root route.
Source: https://github.com/heartcombo/devise/wiki/How-To:-Require-authentication-for-all-pages
So i've built my first app using Devise! I'm pretty stoked, but I would like to know how one goes about having the app re-direct to a specific page after logging in?
In other words,
Instead of logging in, and remaining at the home page, how do I get rails to redirect to a microposts page for example?
In my case specifically it only redirects to the posts page sometimes, and other times it just stays at the initial home page.
Here is my posts controller:
class PostsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :index]
def posts
#title = "Posts"
end
end
By default, devise redirects you to the root, you can customize after_sign_in_path_for method anyway you like. There's also after_sign_out_path_for method at your disposal to customize.
ApplicationController < ActionController::Base
# extra stuff
def after_sign_in_path_for(user)
if something
posts_path
else
root_path
end
end
def after_sign_out_path_for(user)
new_some_other_path
end
end
I am trying to require an authorization in my controller called purcahses_controller.rb It's just an order form model with Show Edit Destroy.. I can view it in active_admin, the url being localhost/admin/purchases. But the problem is I can also view it at localhost/purchases. It lists all of the orders and allows all functions. Id like to route_to rooturl with a simple unauthorized message if the user isn't logged in.
You can do the following and it will give you option to redirect to any path you see fit.
In the file
config/initializers/active_admin.rb
Make the following change
config.authentication_method = :authenticate_active_admin_user!
Then inside your application controller adding something like this:
class ApplicationController < ActionController::Base
protect_from_forgery
def authenticate_active_admin_user!
authenticate_user!
unless current_user.superadmin?
flash[:alert] = "Unauthorized Access!"
redirect_to "/admin/login"
end
end
end
In your purchases_controller add the following before_filter line:
before_filter :authenticate_active_admin_user!
Hopefully this helps!
~Kevin
authenticate_active_admin_user gives you access to the admin authenticate user which will lead you to the authorization regardless of what you call in admin.
controller do
skip_before_action :authenticate_active_admin_user, only: :action
end
https://www.rubydoc.info/gems/nsm-activeadmin/0.2.2/ActiveAdmin%2FResourceController%3Aauthenticate_active_admin_user
https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/base_controller.rb#L38