Multiple forms of authentication for a single field - ruby-on-rails

I currently have a rails project that looks to let customers authenticate their account in two separate ways. Either they can use a bar code that they have stored in the database, or they can use a phone number. I want to merge these two fields so that there will be one field that allows two sources of authentication. Here is my code for the two fields.
def User.authenticate(barcode)
if user = find_by_barcode(barcode)
user
end
end
def User.authenticate2(phone_number)
if user = find_by_phone_number(phone_number)
user
end
end
Any suggestions would be greatly appreciated.

I would suggest using this one for simplicity of code:
def User.authenticate(barcode_or_phone_number)
find_by_barcode(barcode_or_phone_number) || find_by_phone_number(barcode_or_phone_number)
end
However it might cause 2 sql to be queried. So you may consider this:
def User.authenticate(barcode_or_phone_number)
where("users.barcode = ? OR users.phone_number = ?", barcode_or_phone_number, barcode_or_phone_number).first
end

Related

How to restrict a user from updating certain fields with CanCanCan?

So if I have a User, and he can create/update his Service, but he cannot :publish(bool) them, what would be the best solution to skip this field from the update_params hash?
I found this similar question, but it links to CanCan 2.0 which never got released, and I was wondering if there is a way to solve this problem using CanCanCan, but could not find anything in their documentation. Many thanks!
As far as I know you can't. I would have a seperate publish action. So I'd omit publish from the form and the service_params entirely.
Then I'd do something like this:
Class ServicesController
def publish
#service = Service.find(params[:id])
#service.update_column(:publish, true)
end
end
Then in ability
can :manage, Service
cannot :publish, Service
Alternatively you could do this (assuming you have a current user and something defining them as an admin)
def service_params
if current_user.admin?
params.require(:service).permit(:my_field, :publish)
else
params.require(:service).permit(:my_field)
end
end
So you'd be omitting it from the parameters if they're not an admin. In this case you'd probably want to hide the fields in the view dependent on whether or not they can change the field.

Having two different sort of validate, from user or app itself in rails

My program is trying to create some groups automatically, with a prefix of 'automated_group', it won't show up when loading all groups, can't be edited, and some more stuff. But I have to limit users from doing it.
But if I make a validate function, it won't let my app do it and group.save returns false. Even when updating other attributes, it won't let me save it, cause the name won't validate.
Is there any other way? Sometimes use validation, or maybe check who's changing the value?
Thanks in advance
You can use a permission system like cancan (https://github.com/ryanb/cancan). Then you can define someting like this:
can :manage, Group, automated_group: false
I've found the half of the answer in skip certain validation method in Model
attr_accessor :automated_creation
validate :check_automated_name, unless: :automated_creation
def check_automated_name
#...
end
and in my controller:
def get_automated_group
name = "automated_group_#{product.id}"
group = Group.find(name: name).first
group Group.new(automated_creation: true) unless group.blank?
returrn group
end
When updating:
I'll check in check_automated_name function that it any change on name has happened with:
group.name_changed?
so any thing else can be updated except 'name', which the only way of creation is from rails itself.

using .dup with multiple records

I am creating a Stripe payment engine for the Catarse project. I have three records I need to copy from my User, who is a project owner to my User's project. But because I'm a beginner, the code looks like S&%#te!
#project_controller.rb
def create
...
check_for_stripe_keys
....
end
def show
...
check_for_stripe_keys
....
end
....
def check_for_stripe_keys
if #project.stripe_userid.nil?
#project.reload
#project.stripe_access_token = #project.user.stripe_access_token.dup
#project.stripe_key = #project.user.stripe_key.dup
#project.stripe_userid = #project.user.stripe_userid.dup
elsif #project.stripe_userid != #project.user.userid
#project.stripe_access_token = #project.user.stripe_access_token.dup
#project.stripe_key = #project.user.stripe_key.dup
#project.stripe_userid = #project.user.stripe_userid.dup
end
#project.save
end
....
I only need those three records because my stripe code is an engine. Three things:
1) Initially I thought to user update_attributes but I don't know if its possible to use .dup in that method.
2) Is it possible to put this in a helper located in the engine thats accessible to the main_app so users don't have to edit the main_app project_controller code?
3) Is there a cleaner way to show the above .dup code? 'Cause it's fugly!
Thanks for your help!
This is a more compact way to write what you already have:
[:stripe_access_token, :stripe_key, :stripe_userid].each do |field|
#project.send("#{field.to_s}=", #project.user.send(field).dup)
end
Maybe something like this:
Gemfile:
gem 'deep_cloneable'
Code:
#user.dup :include => [:stripe_userid, :stripe_access_token, :stripe_key]
I didn't quite follow your relationships. But take a look at this answer: What is the easiest way to duplicate an activerecord record?
Here is deep_cloneable:
https://github.com/moiristo/deep_cloneable

Rails - add scope for certain controllers in a module based up a database setting

I have an api. In that api is a basecontroller that all other controllers inherit from. The basecontroller handles authentication and whether or not the API is on at all, etc.
There are users, and users can belong to a group. Users table has a group_id column.
I'm trying to introduce a new feature, whereby a select on the settings page for admin controls which users are shown from what groups. If an option is selected, only users from that group should be shown by the api.
I could go into each controller (there is one controller for each of a few different tasks - getting all users info, just active_users ids, a single users information, etc) and add the extra statement to each
if !settings.api_group.nil?
#add the additional where("group_id = ?, settings.group_id)
but that seems like a lot of repeating myself (doing it in 8 different places)
Is there some way to add something to the basecontroller that says:
if this setting option is not nil, only return user information if they are in this group
?
Thanks
You can add a method to the BaseController, and call it in each action that should have this restriction. Something like this:
in base_controller.rb:
protected
def filtered_users
if settings.api_group
User.where(:group_id => settings.group_id)
else
User.scoped
end
end
and in the controllers that inherit from it:
def index
#users = filtered_users
end
This way, you only define the filtering in one place. If it needs to change later, you only have to change it in one place. Because filtered_users actually returns a Relation, you can continue to alter the query by tacking additional .where clauses, etc, like this:
#users = filtered_users.joins(:posts).where('posts.created_at > ?', 1.week.ago)
FYI my answer was exactly what I thought it might have to be in the initial post. I'd love for there to be a more DRY solution, but I ended up doing something like this:
IN USER MODEL
def find_in_api_group
# NOTE settings.api_group is a string => "1,2,4"
if settings.api_group.nil? || settings.api_group.blank?
where("") # THERE HAS TO BE BETTER WAY OF SAYING THIS WITHOUT INTERRUPTING THE CHAIN
else
where("group_id IN (?)", settings.api_group)
end
end
IN VARIOUS CONTROLLERS
user = User.find_in_api_group
#then chain various error tests and additional activeRecord statement

random loop with conditions in rails

I have a feature called "Browse" that allows users to browse through random profiles. When a user clicks on "browse" they are immediately taken to a users profile that they are NOT already friends with. What should my controller look like?
Right now I've got:
def browse
#users = User.all.offset(rand(current_user.matches.count))
#users.each do |user|
if !current_user.friends.include?(user)
#user = user
return
end
end
end
However that doesn't seem to be working. Any advice? I am admittedly bad with blocks, it seems!
You could try something like this
def browse
#user = (User.all - current_user.friends).sample
end
A better version would be
def browse
#user = User.where('id not in (?)', current_user.friends.map(&:id))
.offset(rand(current_user.matches.count)).limit(1)
end
Also, if you are too concerned about performance, instead of using the offset technique, better use the randumb gem to fetch the random record. It uses database specific functions for selecting random records, if available.
Add an extra method to your User, something like this:
def random_stranger
self.class.where(%Q{
id not in (
select friend_id
from friends
where user_id = ?
}, self.id).
order('random()').
limit(1).
first
end
Then in your controller:
def browse
#user = current_user.random_stranger
end
If your database doesn't know how to optimize that not in then you could replace it with a LEFT OUTER JOIN combined with WHERE friend_id is null.

Resources