Learning rails development and would usually prefer to search out an answer than waste peoples time but this has been doing my head in all night.
Essentially I'm trying to present user-dependant views ala github etc.
I'm trying to follow the instructions laid out here:
http://collectiveidea.com/blog/archives/2011/05/31/user-centric-routing-in-rails-3/
My authentication at the moment is from the railscast "Authentication from Scratch - revised" which uses sessions, my sessions_crontroller.rb:
class SessionsController < ApplicationController
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 root_url, notice: "Logged in!"
else
flash.now.alert = "Email or password is invalid"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, notice: "Logged out!"
end
end
And my routes.rb:
C::Application.routes.draw do
root :to => "static_pages#home", :constraints => LoggedInConstraint.new(false)
root :to => "users#show", :constraints => LoggedInConstraint.new(true)
resources :users
resources :sessions
As per my understanding, because I'm not using cookies the final comment under that blog posts recommends using request.session[:your_key] in place of request.cookies.key?("user_token") however when logged in I am still taken to static_pages#home? If anyone could shed some light on the topic I would very much appreciate it.
I also apologise for any formatting errors etc, this is my first question on stackoverflow.
Thanks again!
Not sure about your exact question, but I just did something kind of similar to this, so maybe my code will help you:
My routes:
# Except from config/routes.rb
require File.expand_path("../../lib/role_constraint", __FILE__)
MyApp::Application.routes.draw do
mount Resque::Server, :at => "/resque", :constraints => RoleConstraint.new('admin')
...
...
...
My constraint:
# lib/role_constraints.rb
class RoleConstraint < Struct.new(:value)
def matches?(request)
request.session[:role] == value
end
end
My sessions controller:
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
before_filter :require_user, :only => :destroy
def new
end
def create
user = User.find_by_username(params[:username])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
# Just for /resque
# Not secure - if you change a user's role, it will not be updated here
# until they log out and log in again.
session[:role] = user.role
if user.email.nil?
redirect_to user, :notice => "Please add your email address to your account"
else
redirect_to root_url, :notice => "Logged in!"
end
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
session[:user_id] = nil
session[:current_project_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
Related
I'm still working on my very first Ruby on Rails project. The latest issue I'm dealing with is the user management system. I tried using Devise but I couldn't get past a certain error so I decided to create my sign in/login system manually. I'm using Ruby 3.0.2 w/ Rails 7.0.0alpha2.
I got a lot farther this manual way but I'm getting 'undefined method `cost' for BCrypt::Engine:Class' I've googled and they mention changing the version of bcrypt, I did with no luck
They mention going to my user_controller and changing my create method from old code to this '#user = User.new(user_params)' doing this changed my error from 'forbidden' to the error I'm currently writing to you about.
Not to sure on where my issue is, my guess is somewhere in the model or controller, thank your for your time!
User Controller
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank your for signing up!"
else
flash[:danger] = 'Invalid email/password combination'
render "new"
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Session Controller
class SessionsController < ApplicationController
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, notice: "logged in!"
else
flash.now.alert = "Email or passwrod is invalid"
render "new"
end
end
def destroy
session.delete(:user_id)
#current_user = nil
end
end
User Model
require 'bcrypt'
class User < ApplicationRecord
include BCrypt
validates :email, uniqueness: true
has_secure_password
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Routes
Rails.application.routes.draw do
get 'sessions/new'
resources :users, only: [:new, :create]
resources :fighters
resources :sessions
get 'home/about'
root 'home#index'
get 'signup', to: 'users#new', as: 'signup'
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
end
Error
User Controller
User Model
Routes
Session Controller
I logged on this morning and all of a sudden my problem was fixed! Maybe something having to do with the terminal closing because I compared the code I posted here and what I have in my files, very little changes.
1 change I made was adding # to 'session[:user_id] = user.id' to make '#user.id'
After I was able to log on, I was getting an error to log off. The change I made was adding 'session.clear' and 'redirect_to login_path' in my destroy method.
Both changes made in my sessions_controller.rb
I also added "delete 'sessions', to: 'session#destroy'" to routes.rb
I am learning to refactor my code but I am having trouble refactoring a sessions_controller I have in my application.It is violating the "tell don't ask" principle.I am thinking of extracting some logic to its own class but not sure how that would work.Here is the code from the controller.
class SessionsController < ApplicationController
def create
admin = Admin.find_by(email: params[:sessions][:email])
if admin && admin.authenticate(params[:sessions][:password])
sign_in admin
redirect_to anasayfa_path
flash[:success] = 'Başarılı şekilde giriş yapıldı'
else
redirect_to root_path
flash[:error] = 'Giriş bilgilerinde bir hata var'
end
end
end
How would I refactor this?I thought of extracting
admin && admin.authenticate(params[:sessions][:password])
from this method but would that be the best way?Where would I put the class if I extracted this?
First, you could extract admin lookup, as it may be reused in other actions.
Also, you could standardize your flash keys : notice and alert are two standard keys that #redirect_to understands.
class SessionsController < ApplicationController
before_filter :find_admin
def create
if #admin.authenticate(params[:sessions][:password])
sign_in #admin
redirect_to anasayfa_path, notice: 'Başarılı şekilde giriş yapıldı'
else
redirect_to root_path, alert: 'Giriş bilgilerinde bir hata var'
end
end
private
def find_admin
#admin = Admin.where(email: params[:sessions][:email]).first or redirect_to( root_path, alert: 'not logged in' )
end
end
You have to use #where instead of #find_by to avoid exception if admin is not found.
If you want to keep your current flash keys, you can add in an initializer :
ActionController::Flash.add_flash_types( :success, :error )
Path in #redirect_to from #find_admin and when authentication fail should probably points to log in url.
Try this
class SessionsController < ApplicationController
def create
admin = Admin.find_by(email: params[:sessions][:email])
login_status = false
if admin && admin.authenticate(params[:sessions][:password])
sign_in admin
login_status = true
end
login_status ? redirect_to(anasayfa_path, :flash => {:success => 'Başarılı şekilde giriş yapıldı'}) : redirect_to(root_path, :flash => {:error => 'Giriş bilgilerinde bir hata var'})
end
end
i followed the revised railcast and then decided to upgrade my code with a remember feature. Almost work but When going to the login page I get the following error
Couldn't find Customer with auth_token =
Note I change the table from User to Customer
Here my code, maybe i made a little mistake. I did a reset on the database
Customer Controller
class CustomersController < ApplicationController
def new
#customer = Customer.new
end
def create
#customer = Customer.new(params[:customer])
if #customer.save
session[:customer_id] = #customer.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
end
Session Controller
class SessionsController < ApplicationController
def new
end
def create
customer = Customer.find_by_email(params[:email])
if customer && customer.authenticate(params[:password])
if params[:remember_me]
cookies.permanent[:auth_token] = customer.auth_token
else
cookies[:auth_token] = customer.auth_token
end
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
cookies.delete(:auth_token)
redirect_to root_url, :notice => "Logged out!"
end
end
Route.rb
get 'signup', to: 'customers#new', as: 'signup'
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
resources :sessions
resources :customers
root :to => 'sessions#new'
Application Controller
class ApplicationController < ActionController::Base
protect_from_forgery
private
def authorize
redirect_to login_url, alert: "Not authorized" if current_customer.nil?
end
def current_customer
#current_customer ||= Customer.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_customer
end
Thanks everyone
My mistake was in the following code
def current_customer
#current_customer ||= Customer.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
and fixing it by the following
def current_customer
#current_customer ||= Customer.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
I am working on a Rails App that uses Devise as the authentication module, however I want to customize it so that CanCan will only permit Administrators to create a new user. I am having a hard time understanding how to customize the controller for Devise so that this can be done. Any help would be appreciated.
You don't need to customize anything :D
Remove :registerable from your Devise model.
Create your Users CRUD* (just scaffold users)
Use CanCan for user permission on your Users
Controller.
*Check Devise's wiki on how to create a Users CRUD, there is a routing trick you need to do
You can create a "User" controller that will manage users and then simply set permissions for it. So in your new User controller you can have something like:
class UserController < ApplicationController
load_and_authorize_resource
def index
end
def new
end
def show
end
def create
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def edit
end
def update
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update_attributes(params[:user])
flash[:notice] = "Successfully updated User."
redirect_to user_index_path
else
render :action => 'edit'
end
end
def destroy
if #user.destroy
flash[:notice] = "Successfully deleted User."
redirect_to user_index_path
end
end
end
Assuming you have administrators set to:
can :manage, :all
You should be good to go.
In your routes file you'll need to set up your routes:
resources :user, :controller => "user"
Hope this helps!
This should be simple fix, but I've been unable to find an answer. Can anyone point me in the right direction?
I'm implementing a Rails3 beta invite system a la Ryan Bates - http://railscasts.com/episodes/124-beta-invitations
I've set up the mailer to send out the invite urls. Everything works fine, apart from one small issue.
The url generated by the mailer is /user/sign_up.token.
I need to generate /user/sign_up/token (slash instead of period).
I guess I need to change the syntax in "Mailer.invitation().deliver", but I can't find any documentation to help. Can anyone point me in the right direction?
The relevant bits of my routes file:
devise_for :users, :path_prefix => 'registration', :controllers => {:registrations => 'users/registrations'} do
get "registration/users/sign_up/:invitation_token" => "users/registrations#new"
end
Invitations controller:
class InvitationsController < ApplicationController
def new
#invitation = Invitation.new
#title = "Invite a friend"
end
def create
#invitation = Invitation.new(params[:invitation])
#invitation.sender = current_user
if #invitation.save
if user_signed_in?
Mailer.invitation(#invitation, new_user_registration_path(#invitation.token)).deliver
redirect_to root_url, :notice => "Thank you, your friend will receive their invitation soon."
else
redirect_to root_url, :notice => "Thank you, we'll let you know when the next batch of invites are availale."
end
else
if current_user.invitation_limit > 0
render :action => 'new', :alert => "Sorry, there was a problem! Please try a again."
else
redirect_to root_url, :alert => "Sorry, you don't have any invitations left. Please wait until we issue more."
end
end
end
end
Mailer:
class Mailer < ActionMailer::Base
def invitation(invitation, sign_up)
subject 'Invitation'
recipients invitation.recipient_email
#greeting = "Hi"
#invitation = invitation
#signup_url = sign_up
#sender = invitation.sender_id
invitation.update_attribute(:send_at, Time.now)
end
end
Thank you for any ideas!
Not completely sure if this would work, but maybe try
new_user_registration_url(#invitation.token)
instead of new_user_registration_path.
Another (but not a very good) method would be
new_user_registration_url+"/#{#invitation.token}" #substitute path for url maybe
Hope this helps!
Change your route to
get "registration/users/sign_up/:id" => "users/registrations#new"
and add this to your Invitation model:
def to_param
"#{token}"
end
Then you can simply use
new_user_registration_url(#invitation)