rails 4 - role based authorization from scratch - ruby-on-rails

I have recently started learning rails and had to develop an application with three roles: user(admin), student, instructor. I wrote the following code myself. but the problem is that it makes the role of all active users similar to the most recent login. For example, if admin was logged in and later a student logs in and then if admin refreshes her page, she will also become a student. What i should change in my code to fix this problem? And if it is not possible to implement it like this way, suggest me how i should fix this problem. thanks.
here is my users_controller code:
def login
if session[:user_id]!=nil
redirect_to(:action => 'index')
end
end
def attempt_login
if params[:username].present? && params[:password].present?
found_user = User.where(:username => params[:username]).first
if found_user
authorized_user = found_user.authenticate(params[:password])
if authorized_user
flash[:notice] = "Welcome! You are LoggedIn"
session[:user_id] = authorized_user.id
redirect_to(:action => 'index')
return
end
end
found_student = Student.where(:username => params[:username]).first
if found_student
student_id = Student.authenticate( params[:username],params[:password])
if student_id
flash[:notice] = "Welcome! You are LoggedIn"
session[:user_id] = student_id
redirect_to(:action => 'student_index')
return
end
end
found_instructor = Instructor.where(:username => params[:username]).first
if found_instructor
instructor_id = Instructor.authenticate( params[:username],params[:password])
if instructor_id
flash[:notice] = "Welcome! You are LoggedIn"
session[:user_id] = instructor_id
redirect_to(:action => 'instructor_index')
return
end
end
flash[:notice] = "Invalid username/Password combination."
redirect_to(:action => 'login')
end
end
def logout
flash[:notice]="Logged out"
session[:user_id] = nil
redirect_to(:action => "login")
end
and here is my application_controller.rb code
def require_login
unless session[:user_id] or config.my_config
flash[:notice] = "You are not Logged In"
redirect_to :root
return false
else
return true
end
end

It seems to me that you're sharing the same session key session[:user_id] to store the ID of User, Student and Instructor. How do you differentiate between the three kinds of logged in users?
It's possible that if a logged in Student visits the instructor_index path, then the student's ID would be used to load a Instructor with the same ID, and a view for an instructor would be rendered. I don't see any code to indicate otherwise.
I would have expected such a system to store both a session[:user_id], and a session[:user_type] to ensure that it's possible to exactly identify the logged in person.

Related

Regarding Rails Application Login

I am trying to deploy my rails application. I have a small requirement here.
I am not using Devise. I am providing user to login via his username and password. But now I also want to provide user to login via his email too.
How can I make this work. It should work for username and also for email.
My access controller login action is like this:
def attempt_login
if params[:username].present? && params[:password].present?
p 'Fields Check'
found_user = User.where(:username => params[:username]).first
p "#{#found_user.inspect}"
if found_user
authorized_user = found_user.authenticate(params[:password])
end
end
p "#{authorized_user.inspect}"
if authorized_user
if authorized_user.email_confirmed
# mark user as logged in
session[:user_id] = authorized_user.id
session[:username] = authorized_user.username
redirect_to(:controller => 'users',:action => 'index')
else
flash[:error] = 'Please activate your account by following the
instructions in the account confirmation email you received to proceed'
redirect_to(:controller => 'home',:action => 'index')
end
else
p "Not a Registered User"
flash[:error] = "Invalid username/password combination."
redirect_to(:controller => 'home',:action => 'index')
end
end
Thanks in advance :)
You just have to change the initial check as follows
if (params[:username].present? || params[:email].present?) && params[:password].present?
and then the query to find the user
User.where('username= ? OR email= ?', params[:username], params[:email]).first

session not working in post method in rails

session[:user_id] = user.id this session not working but its working in get method(login)
# Working fine here
# get 'users/login'=>'users#login'
def login
session[:user_id] = 1
end
# post 'users/auth'=>'users#loginpost'
def loginpost
user = User.authenticate(request.POST['name'], request.POST['password'])
if user
# But not working here:(
session[:user_id] = user.id
redirect_to root_url, :notice => "You are Successfully logged in ^^"
else
render html: '<strong>HTML String</strong>'
#render users_login_url, :notice => "You are entered invalid email or password"
end
end

No route matches {:action=>"show", :controller=>"members", :id=>nil}

I set up twitter login system and got the error message above around the codes
redirect_to member_path(session[:user_id]), :notice => "#{auth["info"]["name"]}さんの#{auth["provider"]}アカウントと接続しました。"
in the def callback of the sessions_controller.rb. I wonder why this happened because it never happened when I set up facebook login with omniauth gem.Could you tell me why this occurred and how to solve this?
I checked some elements. A new user was created through twitter login. And I could get "session". But couldn't acquire session[:user_id].
☆member.rb
def self.create_with_omniauth(auth)
create! do |member|
member.provider = auth["provider"]
member.uid = auth["uid"]
member.mail = auth["mail"]
member.image = auth["info"]["image"]
if member.provider == "facebook"
member.name = auth["info"]["name"]
else
member.name = auth["info"]["nickname"]
end
end
end
☆sessions_controller
def callback
#omniauth.auth環境変数を取得
auth = request.env["omniauth.auth"]
#Userモデルを検索
member = Member.find_by_provider_and_uid(auth["provider"], auth["uid"])
if member
# 既存のユーザ情報があった場合ルートに遷移させます
session[:user_id] = member.id
redirect_to member_path(session[:user_id]), :notice => "ログインしました。"
else
# Userモデルに:providerと:uidが無い場合(外部認証していない)、保存してからルートへ遷移させる
Member.create_with_omniauth(auth)
redirect_to member_path(session[:user_id]), :notice => "#{auth["info"]["name"]}さんの#{auth["provider"]}アカウントと接続しました。"
end
end
Your problem is that you try to build a member route which requires id for member. Other words Rails should build routes like /members/:id where id MUST be a number. When you pass session[:id] (which is nil!) to the route helper member_path you get an error.
What to do?
1) (if you use a gem for authentication, for example devise) you can pass to the route helper current_user: memeber_path(current_user)
2) if you have own method which authenticate user you can set session[:user_id] youself.
I suppose that the problem is in else-block. So you can define session[user_id] after a new Member is created. I hope that method Member.create_with_omniauth(auth) return a new member - it means that you need take its id
if member
# 既存のユーザ情報があった場合ルートに遷移させます
session[:user_id] = member.id
redirect_to member_path(session[:user_id]), :notice => "ログインしました。"
else
# Userモデルに:providerと:uidが無い場合(外部認証していない)、保存してからルートへ遷移させる
session[:user_id] = Member.create_with_omniauth(auth).id # <--- UPDATED PART IS HERE
redirect_to member_path(session[:user_id]), :notice => "#{auth["info"]["name"]}さんの#{auth["provider"]}アカウントと接続しました。"
end
PS
And anyway you can avoid to render/redirect to this route if session[:user_id] is nil
redirect_to member_path(session[:user_id]) if session[:user_id]

Devise and Omniauth

I'm trying to integrate Devise with Omniauth and have some troubles with them both. The main problem is to bind Devise User model with Omniauth authentications. I want a simple way to associate my user, with external providers like facebook, twitter, g+ and so on.
One of the most annoying issues that arise with my application is:
If an user registered on my site with devise (I call it local user), that means, provided an email and a password, when user tries to login with twitter, the system asks for mail confirmation. If that mail already exists, then user have to provide another mail. I want instead that he can confirm with a password that it is actually his email. How to do that? How can I override that template?
This is my authentications controller:
class AuthenticationsController < ApplicationController
def index
#authentications = Authentication.all
end
def create
#authentication = Authentication.new(params[:authentication])
if #authentication.save
redirect_to authentications_url, :notice => "Successfully created authentication."
else
render :action => 'new'
end
end
def destroy
#authentication = Authentication.find(params[:id])
#authentication.destroy
redirect_to authentications_url, :notice => "Successfully destroyed authentication."
end
def twitter
omni = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omni['provider'], omni['uid'])
if authentication
flash[:notice] = "Logged in Successfully"
sign_in_and_redirect User.find(authentication.user_id)
elsif current_user
token = omni['credentials'].token
token_secret = omni['credentials'].secret
current_user.authentications.create!(:provider => omni['provider'], :uid => omni['uid'], :token => token, :token_secret => token_secret)
flash[:notice] = "Authentication successful."
sign_in_and_redirect current_user
else
user = User.new
user.apply_omniauth(omni)
if user.save
flash[:notice] = "Logged in."
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omni.except('extra')
p session
redirect_to new_user_registration_path
end
end
end
end
I also have no idea where new_users_registration_path is.

User Authentication: Providing separate errors for invalid email/password entry

In my Rails app I'm trying to produce a separate flash.now[:alert] for invalid :email and :password, respectively. So if a user enters the correct email but wrong password, the :alert warns the user of an invalid password and vice versa. Here's what I have in my SessionsController:
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
redirect_to user.profile, :notice => "Logged in successfully"
elsif user.email != params[:email]
session[:email] = #user.email
flash.now[:alert] = "Invalid email. Try again!"
render :action => 'new'
else
session[:password] = #user.password
flash.now[:alert] = "Invalid password. Try again!"
render :action => 'new'
end
end
Rendering this gives me an undefined method for email. Can anyone help me figure out what I'm doing wrong?
DISCLAIMER: Obviously this is a really bad idea as an attacker could keep on trying emails until he found one that did match and then he could start trying passwords for this email he knows exists at your database, but you're asking, so it's up to you deciding to do this or not.
Your authenticate method obviously only returns the user if the email and password did match, change your authenticate method to return a boolean and a user if there is any available. It would look somewhat like this:
def authenticate(email, password)
u = first(:conditions => {:email => email, :state => 'active'})
u && u.authenticated?(password) ? [true, u] : [false, u]
end
Then, at your controller:
def create
result , user = User.authenticate(params[:email], params[:password])
if result
session[:user_id] = user.id
redirect_to user.profile, :notice => "Logged in successfully"
elsif user
session[:email] = #user.email
flash.now[:alert] = "Invalid email. Try again!"
render :action => 'new'
else
session[:password] = #user.password
flash.now[:alert] = "Invalid password. Try again!"
render :action => 'new'
end
end
And this should work.

Resources