Ruby on Rails omniauth and carrierwave - ruby-on-rails

I'm using omniauth ( omniauth-facebook) in my ruby on rails application. I would like to know whether I can retrieve user's city, country and gender information. And how can I take user profile photo with carrierwave,
Thanks.

Using the (Carrierwave Documentation)[https://github.com/carrierwaveuploader/carrierwave] as my reference, you would need to modify your user model to add a new fields to support the carrierwave gem functionality.
Typically this means:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
But it will vary if you are using MongoDB instead of an SQL/ActiveRecord.
You can test this new field exists by initializing a new object in the rails console and examining the fields available.
bundle exec rails c
u = User.new
u.(tab twice quickly to see the available options)
# You are looking for the avatar field
u.avatar
You can set it from console to test it using
File.open('somewhere') do |f|
u.avatar = f
end
Then finally you would add it to the form field in your views once you tested it. You typically have a CRUD for your user so in the Create/Update forms in the views, you modify these forms to include a file form (this is HTML not specifically rails, so you can look up more information using HTML in your search), and once it is added, you will need to modify the corresponding controllers to whitelist the value so it can be saved using these controller actions.
The process is similar to adding other fields to your user object after the initial generation of the user model.

Usually omniauth will return a standard answer that is only concerned with authentication, basically a hash with a combination of email, name/first/last, profile_picture url, username, etc. Some providers give you email, others don't, others only provide some fields if you ask specifically for them through scopes.
For facebook I'm using the following on my omniauth.rb (inside config/initializers/*)
provider :facebook, ENV['FB_ID'], ENV['FB_SECRET'], scope: 'public_profile, email, manage_pages, user_friends', info_fields: 'id, first_name, last_name, link, email', image_size: 'large'
This means that facebook will provide me an email, some basic info such as ID, first name and last name on the response hash (that omniauth takes care of arranging) after a successful oauth authorization. The token it will provide will also be scoped for managing pages although I don't ask for any field related to it initially.
So in your case you would ask for city, country and whatever in info_fields (double checking through their graph explorer that you don't need any extra scope for those fields).
After you get the response through omniauth (which is basically a piece of code written as middleware, that does the oauth2 flow for you - you could do it by yourself as well) you'll have a profile pic url.
You'll want to download that picture.
Using carrierwave you do that either on your controller or module/class by instantiating the column where you have the uploader set and then executing the method .download! passing it the url from where to download:
user.avatar = AvatarUploader.new
user.avatar.download! omniauth_hash['blabla_fields']['blabla_picture_url']
This will download a remote picture url and save it as a regular carrierwave attachment, that you can then access through your model normally.

Related

How to select attributes when devise setting current_user?

At this point, I'm not sure how or where the helper current_user is loaded. I'd like to select only certain properties from the user table, but devise to
select * from users table
I want to do something like select (id, email, additional_stuff) from users
I would like to be able to modify the current_user set by devise so I can optimise my application from a security point.
Using Rails version 7.0.4
RUby Version 3.1.3
Devise is build on top of the Warden gem which handles the grunt work of actually authenticating users.
Fetching the user from the database is done by the Warden::Manager.serialize_from_session method which can be reconfigured.
# app/initializers/devise.rb
config.warden do |manager|
manager.serialize_from_session(:users) do |id|
User.select(:id, :email, :additional_stuff)
.find(id)
end
end
However, I'm very sceptical that this will have any real benefits to security and you'll most likely just end up breaking parts of Devise/the rest of your application. For example authenticating the user for password updates may fail unless you load the password digest.
Make sure you have tests covering your whole Devise implementation before you monkey around with it.
It's hard to know what you actually mean by "Certain class in the application use the current_user object, one such class is template creator" . But that smells like a huge gaping security hole in itself. If this is running code that comes from the user it should not have access to the entire view context (for example the current_user method). If the user has access to the actual object then whats preventing them from querying and getting any missing data?
If you really need this feature you should be sandboxing it in a separate renderer (not using Rails build in render methods) which only has access to the context and data you explicitly pass (like a struct or decorator representing a user) which is deemed safe.

Ruby on Rails: How to validate if on specific page?

In my Ruby on Rails application I am trying to add in validations that will ensure the user has entered a value in a text box. In my system I have a table called Account which stores users' email account information, when they go onto the views/accounts/_form.html.erb page to add a new email account I want to validate the presence of a port number. I can do this through the following code:
validates :port, presence: true
This successfully ensures that users enter their port number, but when a user signs up through the views/users/_new.html.erb page they have to enter only an email address (e.g example#example.com) and the users_controller will then create a record in the Account table for this email address. My problem is that on the views/accounts/_form.html.erb page the port number is required but on the views/users/_new.html.erb it is not.
Is there a way of validating that the user enters the port number if they are on the views/accounts/_form.html.erb page or invoking the create method in the accounts_controller?
I am aware that I could do this through the HTML required validation like so: <%= f.text_field :port, :required => true %> but I need to add in further validation as well as presence, so this is not suitable.
You can create an attr_accessor field that determines if the validation should occur...
class Account < ActiveRecord:Base
attr_accessor :port_needs_validation
validates :port, presence: true, if: -> {port_needs_validation}
Then just set the accessor in your create method...
def create
#account = Account.new
#account.assign_attributes(account_params)
#account.port_needs_validation = true
if #account.save
...
Extract that part of the logic into a form object, check out the legendary 2012 blog entry from CodeClimate. Things have changed since then, the author uses Virtus to build form objects, more popular & up-to-date gems these days are:
reform
dry-rb
active type
but really you can make anything behave like an ActiveModel object
if it's a one-off thing, just do what Steve said in the other answer but that is a sure way to hell, safe-hate and divorce (at least from personal experience) in any slightly teeny weeny bigger project (i.e. you mean to spend some hours more working on it, it's not like you just finished everything and want to go home).
Actually, just use form classes everywhere and avoid model validations & other callbacks at all. You don't want things sending account activation mails or validating your password complexity when you're writing tests and just need a "post" that belongs to "user".
My own favorite personal fuckup due to model callbacks is sending 240.000 "your account has been upgraded/downgraded" emails because of an innocent spelling change update in an account_type attribute migration just because account_type_changed? was true.
So.. Form classes for ever, model callbacks never.
I would not recommend you have model aware of views. In #SteveTurczyn 's solution, an abstract field is introduced into model to identified the which page it come from, which is an good solution.
As from Ruby on Rail MVC, both View and Model talk to the controller, another solution will be have controller handle validation of params before passing the value to create account.

Rails: remember user input in devise registration form after error

I'm using Rails 3.2.16 and devise 3.2.2.
I've added a (hidden) field account_level to my devise registration form. This is populated by a query string in the URL depending on what plan the user chooses.
If the user has an error with the registration form, such as the password do not match or a required field isn't filled in, the registration page then reloads with the errors present. However, the account_level field is now blank and the url does not have the query string on it for me to pull the data from it.
How can I have rails "remember" what was in that field after a page reload if a error occurs?
Bonus sanity check question: Is there a better way to pass which plan the user has choose when registering?
----------- Update -----------
I used a combination of the answers from Shamsul and aelor. I ended up grabbing the value of the query string and saving it in a cookie using javascript. If the page then reloads from a validation error, and the query string on the URL is no longer there, I look for the cookie that was set and grab the value from there to populate the hidden field.
I want to keep as much on the front end, which is why I opted for the JS solution. I also felt like cookies are a more tried and tested vs local storage.
use localStorage.
you can save your plan , when the user clicks it.
localStorage.setItem('plan','premium');
then you can check for the the item and fill up the hidden field.
if(localStorage.getItem('plan') != null){
$('#hiddeninput').val(localStorage.getItem('plan'));
}
and you can delete that plan whenever you like by using
localStorage.removeItem('plan');
In registrations_controller.rb of devise
Store account_level field in a cookies as
def new
if params[:account_level]
cookies[:account_level] = params[:account_level]
end
#other code
end
def create
if resource.save
unless cookies[:account_level].nil?
cookies.delete(:account_level)
#do other coding
end
end
end
In this way account_level field will not blank after reloaing the page.

How to create a Authlogic user via the rails console?

I'm using this code:
User.new({:email => "guy#gmail.com", :password => "pass", :name => "guy"}).save(false)
I keep getting this error:
AWS::S3::MissingAccessKey: You did not provide both required access keys. Please provide the access_key_id and the secret_access_key.
I use paperclip to store user photos to S3 but it's not required to register. Not sure why this is not working.
Authlogic requires password_confirmation, so you need to do new_user=User.new {|u| u.email='his#email.com', u.password=u.password_confirmation='his_password'}.save!
Beware that such a user is not active yet. You can activate them using new_user.activate!
Can you pass on your user model code? As you save user instance by skipping all callbacks/validations, it should not give you an error for photo model. However, if you share your user and photo model code (and observer if you using), I can look into them.

Validate password on change of certain fields in RoR

I am building a RoR 3 app, a community. It has a User model and some fields.
So when a user is updating a certain field, like his/her birthday, I want to validate that the User typed in the password that is the same in the database. This way I know that it is the right user trying to change the birthday.
So I ask you how i can create such a validator.
Also I would like to be able to specify an array of which fields the user has to validate the password to change.
This is actually pretty easy to do once you are familiar with the Rails framework.
models/User.rb
class User < ActiveRecord::Base
validate :correct_password?, :if => :check_password?
def check_password?
[birthday_changed?, other_field_changed?].any?
end
def correct_password?
# without knowing more about how you store the password
# this probably won't work with your code directly
errors.add_to_base("Must provide password") unless password?
errors.add_to_base("Incorrect password") unless password == User.find_by_id(id).password
end
end
Even though building user authentication and authorization is not hard - I would advise to use something like "AuthLogic" or "Devise" gems/plugins which will most likely cover 90% of the functionality that you need. You alsways can customize/add new functionality if needed.
Such plugins will do most of the grunt work for you: generate MVC, create database, do proper security checks, even email password recovery and such.

Resources