I've been beating my head against the wall on this one - I have code that I use to test if a user is trying to access an account they're associated with and if not redirects them to their own account. Basically it's to stop people from accessing via URL routes accounts other than theirs.
This code works without issue on other sites I've built but for some reason it's not working on a new project.
The code in question:
def set_company
if params[:id].nil?
#company = Company.find(current_contact.company_id)
else
#company = Company.find(params[:id])
if #company.id != current_contact.company_id
redirect_to(company_path(current_contact.company_id), alert: 'You can\'t access that account This is not your account!!! This one is.' )
end
end
end
In theory when the if statement returns true, it should redirect to the company_path with the params ID set from the current user. On the second run of this code it should find the company based on the params id and then return false when checking if the company ID doesn't match the current user's company id, but instead I get a "this page has a redirect loop" error.
Instead of hitting the db with the find method - I think you'll be better validating against pure values, and only then performing the find method:
def set_company
if params[:id].nil?
#company = Company.find(current_contact.company_id)
else
redirect_to(company_path(current_contact.company_id), alert: 'You can\'t access that account This is not your account!!! This one is.' ) if params[:id] != current_contact.company_id
#company = Company.find params[:id]
end
end
try this..using present
def set_company
if params[:id].present?
#company = Company.find(current_contact.company_id)
else
#company = Company.find(params[:id])
if #company.id != current_contact.company_id
redirect_to(company_path(current_contact.company_id), alert: 'You can\'t access that account This is not your account!!! This one is.' )
end
end
end
Related
#app/controllers/sessions_controller.rb
class SessionController < ApplicationController
def new
#session = Session.new
end
def fetch
##user = User.session(params [:user])
redirect_to "http://www.google.com"
end
def create
emai = params[:email]
puts emai
user = User.find_by(:email => session[:emai])
#user = User.find_by (params [:email])
#user = User.find_by email: 'abc#xyz.com'
#user = User.find_by(params[:Email])
#if (session[:Email] = user.email)
if (user)
redirect_to "http://www.yahoo.com"
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
redirect_to "http://www.google.com"
end
#redirect_to "http://www.yahoo.com"
end
end
every time i execute my view i get redirected to google.com even though i pass the parameters.
Edit by R Peck:
My logic should send people to Yahoo if the params are set, but still sends to Google, how can I fix this?
Try:
user = User.find_by(:email => params[:sessions][:emai])
You are not getting the value of email if you only call params[:email] you should call parent first before calling the child params[:sessions][:email].
Several things wrong with your code.
Here's what I'd write:
#app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
#session = Session.new
end
def create
email = params[:sessions][:email]
user = User.find_by email: email
url = user ? "google" : "yahoo"
colour = user ? "valid" : "invalid"
notice = user ? "You signed up successfully" : "Your form is invalid"
redirect_to "http://#{url}.com", notice: notice, color: colour
end
private
def session_params
params.require(:session).permit(:session, :params)
end
end
OOP
I think this may be a little advanced but I'll write it anyway, for my own benefit.
Rails is object orientated (it's built on Ruby which is an OOP language). This means that each time you create/call a controller, it should be centered around objects.
A good example for you would be the Devise controllers.
This has a sessions_controller which essentially allows you to CRUD (Create Read Update Destroy) a session. This is the correct way to use a controller.
Your implementation seems to be dealing with a user, rather than a session, and as such you'd be best using a users_controller to fix it:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new
#user.save
end
end
Having said that, it does seem that you're probably going to resolve the issue to make it so that you can use the User to build a new session.
I guess it's best to remember that you have to ensure you're able to appreciate a good structure for your application
I am using Rails 4.
I have subarticles nested into articles. I am storing all of the form data from subarticles in a session when a user needs to create an account before submission.
Here is what I am using (subarticles_controller):
def create
if current_user.nil?
session[:subarticle] = params
redirect_to new_user_session_path
end
Then after the user signs up, it creates the subarticle with the stored params using
if session[:subarticle].present?
#subarticle = current_user.subarticles.create(session[:subarticle]["subarticle"])
session[:subarticle] = nil
flash[:notice] = "Awesome, you are logged in and your answer is undergoing review."
edit_user_registration_path
end
I am having trouble, however, saving the article_id in which the subarticle is created under. Can someone point me in the right direction to doing this?
A better approach could be to save the (sub)articles created by guest users in the database.
class SubArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
# ...
def create
#subarticle = Article.new(article_params) do |a|
if current_user
a.user = current_user
else
a.token = SecureRandom.hex
end
end
if #subarticle.save
if #subarticle.user
redirect_to #subarticle
else
session[:after_sign_in_path] = edit_article_path(#subarticle, token: #subarticle.token)
redirect_to new_user_session_path, notice: 'Please sign in to finalize your article.'
end
else
render :new
end
end
def edit
if #subarticle.user.nil? && #subarticle.token != params[:token]
redirect_to root_path, alert: 'You are not authorized.'
end
flash[:notice] = 'Please press save again to publish your post.' unless #subarticle.user
render :edit
end
def update
# The #subarticle.token should be included in the edit form
unless #subarticle.user && #subarticle.token == params[:sub_article][:token]
# let the current user claim the article
#subarticle.user = current_user
end
if #subarticle.update(article_params)
redirect_to #subarticle
else
render :edit
end
end
private
def set_article
#subarticle = Article.find(params[:id])
end
def sub_article_params
params.require(:sub_article).permit(...)
end
end
So here we we instead give the user a link to the edit page for the article where he/she can "finish" the article after logging in.
Since a malicious user could potentially "steal" unclaimed articles by guessing the id and entering the edit url we add a random token which we store in the database with the article and add to the url. Not 100% foolproof but at least better.
To make this work you will also need to add a token field to your form:
<%= form_for(#subarticle) do |f| %>
...
<%= f.hidden_field :token %>
...
<% end %>
The reason you might want to consider this is because session storage is often memory based and if you have a large amount of traffic storing the entire params hash in the session will exhaust the server memory. Also you should reset the session before logging a user in or out to avoid session fixation.
We do have a few issues though - first we don't want to accumulate a bunch of "unclaimed" articles if the user (or a bot) never logs in. The easiest way to do this is to setup a cron job to delete articles over a certain age without an associated user.
You would also want to filter any articles without a user from you show/index action.
I'm trying to stop A particular Admin from being removed from the database with ruby on rails
I have tried a few things but heres the code as it stands
Edit 2 changed User.name to #user.name
Model
after_destroy :can_not_destroy_super_admin
private
def can_not_destroy_super_admin
if #user.name == "super admin"
raise "Can't delete this admin"
end
end
I think its a problem with User.name, but I know its seeing this code because I've had errors raising issues with different code I've tried in here.
I'm aware that this is a relatively crude method for stopping deletion of an admin but it's a simple way of getting what I need done.
Any suggestions or help is very much appreciated.
Edit.1
Here is the destroy method
Controller
def destroy
#user = User.find(params[:id])
begin
#user.destroy
flash[:notice] = "User #{#user.name} deleted"
rescue Exception => e
flash[:notice] = e.message
end
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
I'm guessing your destroy action looks something like this?
def destroy
#user = user.find params[:id]
#user.destroy
end
If this is the case, the user you want to check against in your callback is #user.name, not User.name. You want to ensure that the actual user instance you called destroy on is the same one you're checking the name of.
Edit: As determined in the comments, the callback is actually on the model, I misinterpreted as being in the controller. In the model, to reference the objects name, only name is needed, not User.name or #user.name.
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.
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.