This is my session's controller code
def create
user = User.authenticate(params[:login], params[:password])
if user
session[:user_id] = user.id
redirect_to_target_or_default root_url, :notice => "Logged in successfully."
else
flash.now[:alert] = "Invalid login or password."
render :action => 'new'
end
end
I need a div id="welcomebuttons" located in layouts/application.html.erb to display when the user is not in session (logged out) but disappear completely and remain hidden when the user is logged in. I tried adding javascript:hideDiv_welcomebuttons() to the if user but of course that didn't work.
Could anyone help?
in application layout
<% if session[:user_id].nil? %>
<div id="welcomebuttons">
</div>
<% end %>
I'm using block helper like this (just added them to your application_helper.rb and you're good to go):
# application_helper.rb
def not_logged_in(&block)
capture(&block) unless session[:user_id]
end
def logged_in(&block)
capture(&block) if session[:user_id]
end
#application.html.erb
<div>I'm visible for everyone</div>
<%= logged_in do %>
<div>I'm only visible if you are logged in</div>
<% end %>
<%= not_logged_in do %>
<div>I'm only visible unless you are logged in</div>
<% end %>
You define a current_user method in the application controller:
def current_user
# Look up the current user based on user_id in the session cookie:
#TIP: The ||= part ensures this helper doesn't hit the database every time a user hits a web page. It will look it up once, then cache it in the #current_user variable.
#This is called memoization and it helps make our app more efficient and scalable.
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
and then use it as a condition for an if block in your layout:
<% if current_user %>
<div> <%= "Logged in as #{current_user.email}" %> | <%= link_to 'Home', root_path %> | <%= link_to 'Log Out', logout_path, method: :delete %> </div>
<% else %>
<div> <%= link_to 'Home', root_path %> | <%= link_to 'Log In', login_path %> or <%= link_to 'Sign Up', new_user_path %> </div>
<% end %>
Related
OK, previously I had a problem with a no template error from users#create, now it complete 200 OK however does not redirect at all. Below is my edited users_controller.rb
I have a Signup, Login, Logout rails application with users as a resource. I am trying to save the first user in the database so I can then login but this error is server output when I try to "users#new" and "users#create" the full error is below, then my users_controller.rb and views/users -> new.html.erb
No template found for UsersController#create, rendering head :no_content
Completed 204 No Content in 35ms (ActiveRecord: 0.5ms)
users_controller.rb
def new
#user = User.new
end
def create
#user = User.new(user_params)
if (#user = User.find_by_email(params[:email]))
flash[:success] = "User already exists."
if #user.save
session[:user_id] = user.id
flash[:success] = "New User created."
redirect_to '/layouts/application'
else
render 'new'
end
end
end
new.html.erb
<h1>Sign Up</h1>
<%= form_with(model: #user) do |f| %>
<p> Username:</br> <%= f.text_field :username %> </p>
<p> Email:</br> <%= f.text_field :email %> </p>
<p> Password:</br> <%= f.password_field :password%></p>
<%= f.submit "Signup" %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
Do I have to have another html.erb file? And how can I tell what that has to be? Sorry for the obvious question, newb here.
As per your code if the User is not present it will not enter in the if block. Rails end up trying to find create.html as the current action is create.
To avoid this you must redirect it somewhere or render a template which you have done in the next if and else but it's not executing.
The condition is not letting it redirect to anywhere. Try moving the if block out like this.
def create
#user = User.new(user_params)
if User.exists?(email: params[:email]) # I think this should be `user_params[:email]` instead of `params[:email]`
flash[:error] = "User already exists."
redirect_to 'whereever/you/want/to/redirect' and return
end
if #user.save
session[:user_id] = user.id
flash[:success] = "New User created."
redirect_to '/layouts/application'
else
render 'new'
end
end
In my rails app , when I logout , in the destroy method I am setting session[:user_id]=nil. But when I press back button on the browser the session[:user_id] gets back its previous value and it is automatically showing the logged in page. Why is this happening? How do I make the session[:user_id]=nil persistent till I change it?
session_controller.rb
class SessionsController < ApplicationController
def index
end
def show
end
def new
end
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to user_posts_path
else
render 'new'
end
end
def destroy
session[:user_id] = nil
end
end
application.html.erb
<% if !(session[:user_id].nil?)%>
Logged in as <%= current_user.email %>
<%= link_to 'Log Out', session_path(current_user), :method => :delete %>
<% else %>
<% if current_page?(new_user_path) %>
<%= link_to "Log in", login_path %>
<% elsif current_page?(login_path) %>
<%= link_to "sign up",new_user_path%>
<% else %>
<%= link_to "Log in", login_path %>
<%= link_to "sign up",new_user_path%>
<% end %>
<% end %>
<%= yield %>
there is no error in the rails s console.
last message on the console.
Started DELETE "/sessions/2" for 127.0.0.1 at 2015-10-08 00:23:11 +0530
Processing by SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"B0QLdVrsV9ZgwjS/Y8qVb3ID0q9gsC2peFQAZ/0J638kUTpXcAYcg1I+ulX1UaLujr4C7NPgIann74UETMOz6w==", "id"=>"2"}
Rendered sessions/destroy.html.erb within layouts/application (0.1ms)
Completed 200 OK in 144ms (Views: 143.4ms | ActiveRecord: 0.0ms)
Use reset_session in your logout action instead. This will issue a new session identifier and declare the old one invalid and prevents other session fixation based attacks.
http://guides.rubyonrails.org/security.html#session-fixation-countermeasures
This is a run through of how to setup your SessionsController properly:
Sessions are not really like a standard crud resource where you have the full range of CRUD verbs and fetch records from the database.
From a user standpoint there are only three actions:
new - displays the login form
create - verifies the credentials and signs the user in.
destroy - logs user out by resetting the session.
Change your routes definition to treat Sessions as a singular resource:
resource :sessions, only: [:new, :create, :destroy]
Then we are going to create a helper:
module SessionsHelper
def current_user
#user ||= User.find!(session[:user_id]) if session[:user_id]
end
def user_signed_in?
!current_user.nil?
end
def can_sign_in?
user_signed_in? || current_page?(new_user_path) || current_page?(new_session_path)
end
end
This way the actual implementation of how the user is stored in the session is only in one place in your application and not spread all over your controllers and views.
Lets make sure we can call it from our controllers:
class ApplicationController < ActionController::Base
include SessionsHelper
end
Then lets remedy the controller:
class SessionsController < ApplicationController
# GET /session
def new
end
# POST /session
def create
reset_session # prevents sessions fixation!
#user = User.find_by(email: params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to user_posts_path
else
render 'new', flash: "Invalid username or password."
end
end
# DELETE /session
def destroy
reset_session
if user_signed_in?
flash[:notice] = 'You have been signed out successfully.'
else
flash[:error] = 'You are not signed in!'
end
redirect_to root_path
end
end
application.html.erb
<%= render partial: 'sessions/actions' %>
<%= yield %>
We use a partial since the application layout tends to turn into a monster.
sessions/_actions.html.erb.
<% if user_signed_in? %>
Logged in as <%= current_user.email %>
<%= link_to 'Log Out', session_path, method: :delete %>
<% else %>
<%= link_to 'Log in', new_session_path if can_sign_in? %>
<% end %>
My app allows a user to log in from two different places, the header and the new session page. The new session page logs a user in and redirects them to the correct page, but the home pages just reloads the home page without redirecting the user or logging them in.
This is my sessionscontroller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
redirect_back_or feed_user_path(user)
else
session[:user_id] = nil
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
new.html.erb
<%= form_for(:session, url: sessions_path) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Log in", class: "btn btn-large btn-info" %>
<% end %>
Code in my _header.html.erb
<%= form_for :session, :url => {:controller => "sessions", :action => "new"} do |f| %>
<div class="home-login form-group">
<%= f.label :email %>
<%= f.text_field :email, :placeholder => "Email" %>
</div>
<div class="home-login form-group">
<%= f.label :password %>
<%= f.password_field :password, :placeholder => "Password" %>
</div>
<div class="form-group home-login">
<%= f.submit "Log in", class: "btn-info" %>
</div>
<% end %>
This is what shows up in the terminal
--- !ruby/hash:ActionController::Parameters
utf8: ✓
authenticity_token: AegIbI8c1TIddIBPVWTt/B2CBoCAgbJxL+NWDe782Cc=
session: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
email: ** # **
password: ****
commit: Log in
controller: pages
action: home
EDIT****
This is my application controller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
end
Home is a static page in my pages controller
class PagesController < ApplicationController
def home
end
end
EDIT****
This is my SessionsHelper
module SessionsHelper
def signed_in?
!!session[:user_id]
end
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def current_user=(user) # set current user
#current_user = user # session[:user_id] = user.id
end
def current_user?(user) # get current user
user == current_user
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
session.delete(:return_to)
end
end
EDIT****
Routes
sessions POST /sessions(.:format) sessions#create
new_session GET /sessions/new(.:format) sessions#new
session DELETE /sessions/:id(.:format) sessions#destroy
try changing your form_for tag to direct it to the create action with a method of post. something like this:
<%= form_for :session, url: session_path, method: 'post', action: 'create' do |f| %>
There's no difference with where do you put your login form unless you refer to any controller-specific data there. I don't see that, so you should be good to go with just copying the exact same form_for call that actually produces working results. At least I don't see anything that prevents doing this.
And the problem is the action. new. Wrong. It's create. "New" is a display page for the form (and uses GET because of that), the actual form's action (data send path) is "create" (that uses POST). HTTP method is picked from the routes, execute rake routes to verify that they are correct.
A form is not bound to a page it's on. Instead, it has its own path, that should accept input data from the form.
I figured it out. The ERB form was wrapped in an HTML form. That was causing the problem.
I have an if statement in my view which displays a log in form if the user is not logged in or a logout button if they are. I am new to ruby and to rails so I dont know how to troubleshoot this properly.
Currently if I log in with the form I get the logged in flash message but the log in form remains. If I create a new user it logs me in automatically upon creation and the log out button then displays in place of the log in form however when I select log out it logs me out, returns me to the home page and flashes logged out but the logout button remains instead of being replaced again by the log in form
application.html.erb
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header>
<div class="wrapper">
<img src="/gfx/logo.png">
<span>Twitter Clone</span>
<% if current_user %>
<%= link_to "Log Out", sessions_destroy_path %>
<% else %>
<%= form_tag sessions_create_path do %>
<%= text_field_tag :username, nil, placeholder: "username" %>
<%= password_field_tag :password, nil, placeholder: "password" %>
<%= submit_tag "Log In" %>
<% end %>
<% end %>
</div>
</header>
<div id="content">
<div class="wrapper">
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, class: "flash #{name}" %>
<% end %>
<%= yield %>
</div>
</div>
<footer>
<div class="wrapper">
Ribbit - A Twitter Clone in Ruby<img src="/gfx/logo-nettuts.png">
</div>
</footer>
</body>
</html>
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_username(params[:username])
if user && user.authenticate(params[:password])
session[:userid] = user.id
redirect_to root_url, notice: "Logged in!"
else
flash[:error] = "Wrong Username or Password."
redirect_to root_url
end
end
def destroy
session[:userid] = nil
redirect_to root_url, notice: "Logged out."
end
end
Well one thing is you are setting session[:userid] in sessions#create but you are checking for it in the current_user with session[:user_id]
session[:userid] equals not session[:user_id]
it is not accidentally mistake
session[:userid] = user.id
to
session[:user_id] = user.id
and
session[:user_id] = nil
I'm trying to set up a simple login using AuthLogic into my User table. Every time I try, the login fails and I don't know why. I'm sure this is a simple error but I've been hitting a brick wall with it for a while.
#user_sessions_controller
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Login successful!"
else
flash[:notice] = "We couldn't log you in. Please try again!"
redirect_to :controller => "index", :action => "index"
end
end
#_user_login.html.erb (this is the partial from my index page where Users log in)
<% form_tag user_session_path do %>
<p><label for="login">Login:</label>
<%= text_field_tag "login", nil, :class => "inputBox", :id => "login",
</p>
<p><label for="password">Password: </label>
<%= password_field_tag "password", nil, :class => "inputBox", :id => "password",
</p>
<p><%= submit_tag "submit", :class => "submit" %></p>
<% end %>
I had Faker generate some data for my user table but I cannot log in! Every time I try it just redirects to index. Where am I going wrong? Thanks everybody.
------UPDATE------
I implemented Jakub Hampl's suggestion with form_for just now - I'm getting a new error.
ActionView::TemplateError (called id for nil, which would mistakenly be 4 -
1: <% form_for #user_session do |f| %>
2: <% if flash[:notice] -%>
3: <p class="notice"><%= flash[:notice] %></p>
4: <% end -%>
app/views/index/_user_login.html.erb:1
app/views/layouts/index.html.erb:65
app/controllers/index_controller.rb:3:in `index'
Rendered rescues/_trace (86.0ms)
Rendered rescues/_request_and_response (1.0ms)
Rendering rescues/layout (internal_server_error)
I have not changed the controller at all. Thank you everyone who is responding to this topic - it's incredibly helpful to me. What can I do now to get past this hurdle?
------UPDATE #2------
Here is my application controller.
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_path
return false
end
end
def require_no_user
if current_user
store_location
flash[:notice] = "You must be logged out to access this page"
redirect_to root_url
return false
end
end
Which one of these should be changed to #user_session?
A good idea is to use form_for if you possibly can:
<% form_for #user_session do |f| %>
<p>
<%= f.label :username %>
<%= f.text_field :username, :class => :inputBox %>
</p>
<p>
<%= f.label :password %>
<%= f.password_field :password, :class => :inputBox %>
</p>
<p><%= f.submit "submit", :class => :submit %></p>
<% end %>
Apart from that hard to say. Does your log file give any detail (aka errors)? Also try to add the errors on the table to you flash so that you can see what's going wrong.
Since it seems fro your last update that #user_session is not set, just go ahead and create one: <% form_for UserSession.new do |f| %>.
in your case, params[:user_session] is empty because it's not being set in your view. I think Jakub Hampl's suggestion to use form_for is the best way, but you can also stay with form_tag by setting input names to user_session[login] and user_session[password], OR you can change the line in your action to #user_session = UserSession.new(:login => params[:login], :password => params[:password])
.