I have a simple user model with an edit page. Currently you can change your email and your password (with a password confirmation) but I don't currently require you to type your password again before changing any of that information.
I have a before filter that requires you to be logged in as well as a before filter to ensure you can only edit your own profile. However, in the case of public computers, I would like to re-authenticate a user by making them type their password.
I am using sorcery to back my authentication. How would I go about doing this. I don't see any methods for checking the password after being logged in. Below is my current users_controller
class UsersController < ApplicationController
before_filter :require_login, :only => [ :edit, :update ]
before_filter :correct_user, :only => [ :edit, :update ]
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url, :notice => "Signed up!"
login_user(#user)
else
render :new
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to root_url
else
render 'edit'
end
end
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user == #user
end
end
I've opened an issue on Sorcery's github page. It sounds like the creator will be opening a method called validate_credentials() in the future.
Source: https://github.com/NoamB/sorcery/issues/34#issuecomment-2108845
Related
In my app, when a user logins he/she is redirected to the users profile page. Say he/she is redirected to http://localhost:3000/users/1
If he/she replaces 1 with any other number I want them to redirect to there
current profile no matter if users exits in the database or not
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
#current_user = nil
reset_session
redirect_to root_path
end
end
User Controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:new, :show, :edit, :update]
before_action :correct_user, only: [:new, :show, :edit, :update]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(set_params)
if #user.save
redirect_to new_sessions_path
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#posts = #user.posts
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(update_params)
redirect_to #user
else
render 'edit'
end
end
private
def set_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def update_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
Currenty if user type in search bar localhost:3000/users/5 and user with id 5 does not exists in database it shows error
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with 'id'=3
but I want to simply redirect to currently logged in users profile page.
If users type in search bar localhost:3000/users/3 and user with this id exists in db , currenty it show an error that firefox is not able to process this request but i want it redirect to its default page i.e,,user's profile page.
Create another controller call it UserController and don't depend on id. Instead figure out the current user from the session and display that user. So the show method for this controller would look like this:
def show
#user = User.find(session["user_id]")
#posts = #user.posts
end
Also, you might want to protect your UsersController by validating if the current user has access to view / update the user being queried for.
Just change your UsersController#correct_user to catch ActiveRecord NotFound exception:
class UsersController < ApplicationController
...
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
rescue ActiveRecord::RecordNotFound
redirect_to(root_url)
end
end
I would use "where" and ".take" in Users show method. The find method brakes the code when it does not find anything
def show
#user = User.where("id" => params[:id]).take
if #user.present?
#posts = #user.posts
else
redirect_to(root_url)
end
end
Or you can redirect instead of root_url to a more friendly error view that shows User not found
i have a rails app with sorcery
everything work .
the problem is when edit a user like :
http://localhost:3000/users/1/edit
its work fine , but when i change the user id to 2 or 3 ..
i can update all users data
how can i restrict the edit page only if the current user is the one that logged in
here is my controller :
skip_before_action :require_login, only: [:new, :create, :show]
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
auto_login(#user)
flash[:info] = "Welcome."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
you can also do something like this
before_action :edit_rights?, only: [:update, :edit]
private
def edit_rights?
#user = User.find(params[:id])
redirect_to(root_path) unless current_user == #user
end
you won't need #user = User.find(params[:id]) in both update and edit actions then
There are (at least) two ways to do that. First and straightforward is detailed in another answer, fine-tune your controller.
A less obvious way is to create a singular resource and its own controller. In routes that could look like:
resource :profile, only: [:show, :edit, :update]
# generates:
# /profile (GET, PATCH, PUT)
# /profile/edit (GET)
Then create a controller that is responible solely for user's own profile and operates only on current_user.
Yes, it's okay for one model to have multiple controllers, if your model should behave really differently in different parts of your app.
Why would you do that?
User's own profile could show much more information than is available publicly, you can lay it out in a separate view
No "access denied" errors, as the resource is auto-selected via current_user, all you need is ensure the user is logged in in the entire controller.
I'm using omniauth to allows users to sign up/sign in. I'm trying to add this on top of a simple authentication log in/log out system that I already have in place. I am not using Devise. How can I have a user who logs in with omniauth have the same status as currently defined :signed_in_user?
I have most of the code set up except I'm trying to figure out how to get the user to actually log in and show their logged in page when doing it with omniauth.
First here's the omniauth authentications_controller which seems to work so far
def create
omniauth = request.env['omniauth.auth']
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:success] = "Signed in successfully"
sign_in_and_redirect User.find(authentication.user_id)
elsif current_user
token = omniauth['credentials'].token
secret = omniauth['credentials'].secret
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => token, :secret => token_secret)
flash[:success] = "Authentication successful"
sign_in_and_redirect current_user
else
user = User.new
user.apply_omniauth(omniauth)
if user.save!
flash[:success] = "Account created"
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omniauth.except('extra')
redirect_to '/signup'
end
end
end
Here's the sessions_controller which is used by the first authentication system
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to root_url
else
flash.now[:error] = "Invalid email/password combination"
render 'new'
end
end
def destroy
sign_out
redirect_to root_path
end
end
This is my sessions_helper
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
current_user = user
end
def sign_in_and_redirect(user)
#what should go here?#
end
Users_controller
Class UsersController < ApplicationController
before_filter :signed_in_user,
only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome!"
redirect_to root_url
else
render 'new'
end
end
With my current authentication system (not omniauth), a redirect to root_url makes the signed-in user go to 'static_pages#home'
class StaticPagesController < ApplicationController
def home
if signed_in?
#post = current_user.posts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
I would move the sign_in helper functions to ApplicationHelper so that you can use them in places other than the SessionsController.
After that, it should be very simple.
def sign_in_and_redirect user
sign_in user
redirect_to root_url # or wherever you want
end
It might even be easier to just do sign_in user and redirect_to root_url instead of using another helper.
I'm working through the Rails Tutorial, by Michael Hartl, and a question popped up, as I was creating an admin user.
I followed the instructions, and created an admin_user, who has access to the :destroy method. It also isn't attr_accessible, so people can't simply put a put request via the browser and change themeselves to admin.
But, I have a two-part question--
1) How would I make a user admin?
I though I would need to write something like this in the console
rails console
user = User.find(params[:101])
user.toggle!(:admin)
When I try that, I get
Undefined Local Variable or Method 'Params' for main:Object
2) Assuming that it is possible to make myself an admin, what's stopping other people from making themselves admin using a command line as well?
Here's a copy of the users_controller, I think Michael addressed this in the tutorial, and I followed his instructions, but I don't get how the below code prevents someone from going to the command line and making themselves admin
class UsersController < ApplicationController
before_filter :signed_in_user,
only: [:edit, :update, :index, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
end
def new
unless signed_in?
#user = User.new
else
redirect_to #current_user
end
end
def create
unless signed_in?
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
else
redirect_to #current_user
end
end
def edit
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
I would really appreciate your help clearing things up!
User.find(params[:101]) is appropriate only for http browser requests. If you visit http://www.example.com?101=test, then you can use params[:101] with value "test". But in console you can't use params unless you declare it. In your case the wright way will be User.find(101), if 101 is user id.
Other people can't make them admin because you didn't add attr_accessible for admin field. How can they do it via command shell? They have no access to command line. If they are it's a serious security breach.
I am trying to block all default methods except create and update in my users controller using declerative_authorization. But at the time I add filter_resource_access or filter_access_to into my usersController i always get "Couldn't find User without an ID". Anyone care to explain why this could be happening?
class UsersController < ApplicationController
filter_resource_access
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Account registered!"
redirect_to account_url
else
render :action => :new
end
end
def show
#user = #current_user
end
def edit
#user = #current_user
end
def update
#user = #current_user # makes our views "cleaner" and more consistent
if #user.update_attributes(params[:user])
flash[:notice] = "Account updated!"
redirect_to account_url
else
render :action => :edit
end
end
end
You should set the #user variable before the filter_access_to call with a before_filter as declarative_authorization tries to access #user when you call filter_access_to.
before_filter :set_user
filter_access_to :all
...
protected
def set_user
#user = #current_user
end
Maybe you are setting the attribute_check parameter to true in your filter_access_to call? I have a similar controller and I don't really need the before_filter.
Another thing that might be causing it is a using_access_control call in your User model.