I want the user to click on an activation link before being "activated" or before they can log in with the email/password.
I am not using an gems and want to keep it that way. My problem is that after the user registers, they can login in without clicking on the activation code. I have an confirmation_token line and a confirmed line to the model.
user controller:
def create
#user = User.new(params[:user])
if #user.save
render "root_path"
else
render "new"
end
end
def confirmed
user = User.find(:first, :conditions => {:confirmation_token => params[:confirmation_token]})
if (!params[:confirmation_token].blank?) && user && !user.confirmed?
user.confirmed!
self.current_user = user
flash[:notice] = "Thank you. You account is now activated."
redirect_to account_preference_path(current_user)
else
flash[:notice] = "Sorry we don't have your email in our database."
redirect_to root_path
end
end
user model:
def confirmed!
self.confirmed = true
self.confirmation_token = nil
save(false)
end
Am I missing anything? Thanks!
I know there are gems like devise, auth-logic, etc out there but I want to learn how to write it from scratch. Thanks.
EDIT:
session controller
def create
user = User.authenticate(params[:email], params[:password])
if user && user.confirmed == true
cookies.permanent.signed[:remember_token]
redirect_to account_path(user.id), :notice => "Welcome, #{user.first_name}"
else
flash.now.alert = "Invalid email or password."
render "new"
end
end
Of course, after much trial and tribulation, I figured it out. Before, I was redirecting the routes to a new controller where they can edit their password instead of just sending them to the route that just confirms the code. Silly mistake that cost me a lot of headache, but live and learn. Thanks everyone who looked into it.
You might want to search for some tutorials to at least guide you through the process, you'll get a better feel for coding rails correctly.
Basically your problem is that your not doing a check to see if the user is confirmed or not on login. One way would be to add this inside your session create method.
if user && user.confirmed?
The best solution though is probably to use filters like this
before_filter :authenticate, :only => [:new, :create, :edit, :destroy]
Then you have an authenticate method that checks for a current user, and then have logic that says the current_user can only be a confirmed user. This will check that the user is valid on all the pages that they need to be, instead of only on login.
Related
Hello I am trying to fix up my user sign up so if a user is already registered with the site with the same email, they cannot sign up. So far this is the code in my controller I am trying to implement.
User controller
class UsersController < ApplicationController
def create
unless User.exists?(:email => params[:email])
#user = User.new(user_params)
if #user.save
#user.cart = Cart.create
#user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
end
I figured that in the users controller I would have an unless conditional so if a user already exists it would prevent that user from signing up and just render the new page again. However the user is still able to sign up. Any ideas on how to do this properly would really help out.
Instead of validating this in your controller, move the validation to the User model, and add this line:
validates :email, uniqueness: true
I'm having a problem with a notice appearing when it shouldn't. When I click a link in my app to login, it's flashing the 'Invalid username/password combination' notice, even though I haven't typed anything in. I understand why I'm getting the message - it's because when I click the link, I haven't typed in a matching username and password, so the error fires. But I'm not sure how to fix this. I want the error to appear when the user does type in the wrong combo, but not when the page first appears.
In the code, the 'user' refers to an admin, and the customer is a customer. I'm using the same login page for both types of people.
Also, what I'd really like is that when the user does type in the wrong combination, their email address will stay in the field so that they don't have to type it in again. How would I go about doing this?
Thanks!!
Here's the updated controller code:
class SessionsController < ApplicationController
skip_before_filter :authorize
def new
end
def create
user = User.find_by_email(params[:email])
customer = Customer.find_by_email(params[:email])
if user and user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to admin_url
elsif customer and customer.authenticate(params[:password])
session[:customer_id] = customer.id
redirect_to customer_path(session[:customer_id])
else
render :new, notice: "Invalid email/password combination"
end
end
def destroy
session[:user_id] = nil
session[:customer_id] = nil
redirect_to store_url, notice: "Logged out"
end
end
Set the flash notice only when login parameters where sent with the request:
# ...
else
flash[:notice] = "Invalid username/password combination" if params[:email] || params[:password]
redirect_to login_url
end
I suggest you wrap everything in if params and rerender the view instead of redirecting to preserve the email.
if params
if user ...
...
elsif ...
...
else
render :new
Try replacing your last else statement with this:
else
if params[:email] || params[:password]
flash[:notice] = "Invalid username/password combination"
redirect_to login_url
end
end
That should do the trick. In regards to your question about the email address remaining in the field when the user enters an incorrect password, check this link: here
The general gist of the solution would be to use 'render' instead of 'redirect_to' in your code to reload the page.
a user of my website must first login to see every page of it. My current solution results in a "too many redirects" error message in the browser, I suspect something goes wrong so it endlessly goes back and forth, but where?
In the application controller I have this:
before_filter :authenticate_user
def authenticate_user
unless session[:user_id]
flash[:alert] = I18n.t :session_unauthenticated
redirect_to login_path
end
end
The "login_path" goes to "sessions/new", which looks like this:
def new
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, :notice => I18n.t(:session_logged_in)
else
flash.now.alert = I18n.t(:session_invalid)
render "new"
end
end
Thanks for your help!
Your before_filter is active for each and every action (since it's in the ApplicationController) and therefore also for your sessions#new action. Meaning you cannot even enter the login action. I suggest you put the before_filter in every needed controller and specify the actions with before_filter :authenticate_user, :except => [:new, :create, :destroy] or whatever you need.
In my update user profile form, the first field asks the user to enter her current password. When she submits the form, I verify the password before accepting the changes in other fields. Here's how I'm currently doing this in the users controller:
def update
#user = User.find(params[:id])
if #user.has_password?(params[:user][:password])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to #user
else
render 'edit'
end
else
flash[:failure] = "Password does not match!"
render 'edit'
end
end
I feel like there's a better way to do this. For instance I could make password matching a validation in the user model. Then formtastic would automatically handle the error message for me (as opposed to my ugly flash approach above). I tried doing this with
validate :password_match?, :on => :update
And
def password_match?
has_password(params[:user][:password])
end
But as suspected params is not accessible from the model.
I searched SO for 20 minutes for a way to do this, couldn't find anything that did not involve Devise or Authlogic. I'm doing authentication from scratch (everything works fine: signin, sessions, etc.).
Please, show me the light on the better way!
You don't need devise, just use a before filter on your controller on update
On your profile controller.
before_filter password_match, :only => :update
then on the bottom as private.
private
def password_match
#user = User.find(params[:id])
#user.has_password?(params[:user][:password])
I know I should put the code in the create action of the users controller, but I'm not sure what code I should put. I also assume it should call the create action in my sessions controller, but again I'm not sure how...
By the way I tried render :template => 'sessions/create' in the create action of the users controller, but I get this error when signing up:
Template is missing
Missing template sessions/create with {:locale=>[:en, :en], :formats=>[:html], :handlers=>[:rjs, :rhtml, :erb, :rxml, :builder]} in view paths "/rubyprograms/dreamstill/app/views", "/rubyprograms/dreamstill/vendor/plugins/facebox_render/app/views"
This is all in my application controller:
protected
# Returns the currently logged in user or nil if there isn't one
def current_user
return unless session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
# Filter method to enforce a login requirement
# Apply as a before_filter on any controller you want to protect
def authenticate
logged_in? ? true : access_denied
end
# Predicate method to test for a logged in user
def logged_in?
current_user.is_a? User
end
# Make logged_in? available in templates as a helper
helper_method :logged_in?
def access_denied
respond_to do |format|
format.html do
flash[:alert] = "You must log in to peform this action."
redirect_to root_path
end
format.js do
render_to_facebox(:partial => 'sessions/login_box')
end
end
false
end
Somewhere in your controllers you have something that looks like this:
user = User.new
# set attributes
user.save
render :template => 'sessions/create' # Probably based on your question
All you need to do is update the session to:
user = User.new
# set attributes
if(user.save)
session[:user_id] = user.id
# Send them somewhere useful
else
# Handle the error
end
They're signed in once session[:user_id] is set.
Technically?
In your controller, after you create your user, this code:
#current_user = user
should get you going (looks like you're using restful_authentication).
Now, whether it's a good idea to log in a user automatically without verifying their email address / whatever else is up for debate.
You seem that you just begin with Rails right ? I would highly recommend that you use a gem like Devise to handle your user registrations.
However, if you insist on doing it manually, you would just need to create a session variable that verifies whether a user is logged in or not. Then, you can add a helper like current_user, to get the user if user session shows he/she is logged in.
I see that you have a sessions controller there. Are you trying to use restful_authentication ? If so, once more i highly recommend switching to Devise :)
OLD CODE USING RESTFUL AUTHENTICATION - SESSIONS CONTROLLER
# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
# Be sure to include AuthenticationSystem in Application Controller instead
include AuthenticatedSystem
# render new.erb.html
def new
end
def create
logout_keeping_session!
user = User.authenticate(params[:login], params[:password])
if user
# Protects against session fixation attacks, causes request forgery
# protection if user resubmits an earlier form using back
# button. Uncomment if you understand the tradeoffs.
# reset_session
self.current_user = user
new_cookie_flag = (params[:remember_me] == "1")
handle_remember_cookie! new_cookie_flag
flash[:notice] = "Logged in successfully"
redirect_to :controller=>'Town'
else
note_failed_signin
#login = params[:login]
#remember_me = params[:remember_me]
render :action => 'new'
end
end
def destroy
logout_killing_session!
flash[:notice] = "You have been logged out."
redirect_back_or_default('/')
end
protected
# Track failed login attempts
def note_failed_signin
flash[:error] = "Couldn't log you in as '#{params[:login]}'"
logger.warn "Failed login for '#{params[:login]}' from #{request.remote_ip} at #{Time.now.utc}"
end
end