Omniauth-Twitter - original profile image - ruby-on-rails-4

I am using Omniauth-Twitter gem to authenticate users and show their profile images. When I attempt to display a full-size user profile image in my users#show view, via link_to method, the image gets resized to 41x41px. Is there any way to get a standard image URL (256x256px)?
My omniauth.rb initializer has the default image size set to original, as follows:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, "...", "..."
{
...
:secure_image_url => 'true',
:image_size => 'original',
...
}
end
and my User model appends the Twitter image URL to a column in the Users table like so:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
...
user.image_url = auth.info.image
...
end
end
end
What I've tried:
Removing :image_size pair from the data hash
Passing width: and height: properties to the link_to
Changing the value of the image_size key to
.extra.raw_info.profile_image_url

The solution I found would probably violate the mighty convention over configuration principle:
remove any calls that determine the image size (i.e. from the URL string by using .gsub! method like so:
"profile_image_path_normal.jpg".gsub!("_normal","") #replaces "_normal" with nothing
Would love to hear other suggestions.

Related

Rails 4 Dynamically Generate an Extra Public-Facing URL on Create

I have a Rails app that stores stock photos in each project. Upon creating a project, I want the app to not only create the url for the project that we will view internally, which is projects#show,i.e. /projects/4 but I also want it to create another URL that we can show to the client that will be a little different. It will allow the to approve the photos, etc. I want the url to be something like /projects/st53d where the end of the url will be a random number generated with random_string = SecureRandom.urlsafe_base64(5)
This way, I can pass this url to the clients and they can view all the photos in the project and approve the ones they want us to use, but cannot change or view the other internal stuff we have on the standard product show page
Currently, I have added a client_url column to the Project model and I was thinking of generating the random number within the Project#create method and then saving it to the project's client_url column. This way I can loop through all client urls and make sure I did not generate a duplicate. But I cannot figure out how to do the route creation part.
i have yet to do the part where I check if it is random but I know how to do that, just not the route creation.
I was thinking I needed to create a new method which I named show_client and somehow create a route for that in my config/routes.rb file
class ProjectsController < ApplicationController
before_action :authenticate_user!, only: [:show,:index]
def create
#project = Project.create(project_params)
#project.creator = current_user.email
require 'securerandom'
random_string = SecureRandom.urlsafe_base64(5)
#project.client_url = random_string
#project.save
redirect_to #project
end
def show_client
#project = Project.find(params[:id])
#photos = #project.photos
end
This seems like more of a roles issue. You could check out something like CanCanCan. If you only need two kinds of users, you could also just add a boolean column for admin to your user table. The url extension doesn't seem to matter in the scope of your problem. If you want to use a different view for the visiting the user show page, something in the show action like:
unless current_user.admin?
redirect_to client_show(current_user.id)
end
I was able to figure it out.
I created the client_url column in the database and upon creating a project, in the create method of the projects_controller, I generated a random base64 number and assigned it to project.client_url
in routes.rb i did:
get 'projects/clients/:client_id' => 'projects#clients', as: 'projects_client'
in my projects_controller.rb:
def clients
#project = Project.where(client_id: params[:client_id])
end
then the link to that route is:
<%= link_to 'Client Version', projects_client_path(#project.client_url) %>

Rails image upload security

Currently, I adopt Carrierwave for users to images.
However, I hardly find a solution for image security, i.e. how to set image authorisation for the uploaded images to only let certain user in the same group to view?
After digging Facebook's implementation, I observe that they inject these params (oh,oe, __gda_) to the image url
?oh=924eb34394&oe=55E07&__gda__=1436393492fc8bf91e1aec5
Is there any similar implementation for carrierwave or paperclip?
Thanks
I worked quite a bit with this (only with Paperclip).
There is one solution that is okay, but it takes a lot of processing.
If you only want to hide your files from being looped through you can hash your Paperclip attachment, see this: https://github.com/thoughtbot/paperclip/wiki/Hashing
If you want to authorize user on every image load you can do like this:
Move your files out of your Public-folder
has_attached_file :image,
styles: { large: '1500x1500>', small: '250x250>'},
path: ':rails_root/storage/gallery/image/:style/:filename'
Use Sendfile to view your file
def show
send_file(object.image.path(:small), filename: object.image_file_name, type: "image/png",disposition: 'inline',x_sendfile: true)
end
I'm however a bit reluctant to implement this for example an image gallery, since it takes a GET-action + authorization for each image. Using the x-sendfile works with Apache to deliver the images faster.
Ref:
http://apidock.com/rails/ActionController/Streaming/send_file
I found this great solution for paperclip from https://makandracards.com/makandra/734-deliver-paperclip-attachments-to-authorized-users-only
Though a little out of date, this article details everything needed to secure not only the access to attachments, but also how to secure the files themselves. This article describes all of the steps to implement it, including Capistrano deployment!
be sure to use updated routes by changing:
map.resources :notes, :member => { :attachment => :get }
to:
resources :notes, only: [] do
member do
get :attachment
end
end
also I updated the link from:
link_to 'Download attachment', [:attachment, #note]
to:
link_to 'Download Attachment', attachment_note_path( #note.id )
also see Paperclip changing URL/Path for configuring the url.
Carrierwave stores uploads in /public by default, where all content is simply served as static content. If you need to control access to this uploads I'd start by configuring a different storage path
class TestUploader < CarrierWave::Uploader::Base
def store_dir
Rails.root.join('uploads', relative_path).to_s
end
def serving_path # Use this method to get the serving path of the upload
File.join '/uploads', relative_path
end
private
def relative_path
File.join model.class.model_name.plural, model.id.to_s
end
end
Since CarrierWave relies on public asset serving to serve uploads, you'll have to implement your own file serving method. This is silly example of how to do that with Rails
class Test < ApplicationRecord
mount_uploader :file, TestUploader
end
Rails.application.routes.draw do
get '/uploads/:model/:id', to: 'uploads#get'
end
class UploadsController < ApplicationController
def get
# ... autorization logic
model = params.fetch(:model).singularize.camelcase.safe_constantize
return head 400 unless model.present?
send_file model.find(params.fetch(:id)).file.path
end
end

undefined method `model_name' for #<User:XXXXXXXX>

I have a dashboard containing 2 partials. One partial works fine and does what its suppose to (bill). The other partial is setup similar to bill but wont work(request). When I look at my log it shows that the tenant(user) is being queried, also, the 1st partial is queried but the 2nd partial doesn't query. when I went to my dashboard controller and changed the instance of the partial to (Request.new) it works but I can't seem to get it to work right thru the controller. I don't want to have the method in the model. I am using mongoid.
SO here is my render in the dashboard...
<%= render partial: "request", locals: {request: #request} %>
In the partial I have...
<%= form_for [:tenants, request] do |f| %>
And on the dashboard controller I have...
def show
#dashboard = current_tenant
#bill = current_tenant.bill || current_tenant.build_bill
#request = current_tenant.request || current_tenant.build_request
end
(if I change #request = Request.new it works fine but I know that's not right)
The bill partial works and the dashboard finds the tenant but I keep getting
"undefined method `request' for #
Any idea of what I am missing? I compared the bill controller to the request controller and I cant find any differences. When I put the Model.new into the dashboard controller it works but I know this isn't right, its as if the app wont recognize the Request controller...
The error is saying it doesn't recognize "request" method.
Also here is my controller for request...
class Tenants::RequestsController < ApplicationController
before_filter :authenticate_tenant!
def index
#requests = Request.all
end
def show
#request = Request.find(params[:id])
end
def create
if #request = current_tenant.create_request(authorization_params)
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} has been Authorized!"
else
redirect_to tenants_dashboard_path, :error => "#{request.manager_name} has NOT been Authorized, please try again."
end
end
def edit
end
def update
if current_tenant.request.update_attributes(authorization_params)
redirect_to tenants_dashboard_path, :notice => "You have approved #{request.manager_name} to review your report"
else
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} is NOT allowed to review your report"
end
end
def destroy
#request = Request.find(params[:request_id])
name = #request.name
if #request.destroy
flash[:notice] = "\"#{name}\" was successfully removed from your profile."
redirect_to #dashboard
else
flash[:error] = "There was an error deleting this managers access."
render :show
end
end
Well it looks like
current_tenant.request has an issue. That means that the method is not available. Assuming you're not trying to access the http request , then you have an issue with the request method.
So your issue is with how you defined the request method (maybe in your model). e.g. is it a class method or a instance method etc.
Without knowing your goal, that's the general answer I can give you. Creating a Request.new could be right depending on your goal, but if your goal is to call the request method, you must make it available to current_tenant
One controller shouldn't be calling your other controller as you have suggested...

Copy a Paperclip image to a new record in Rails 4

My site is for posting album reviews, which are called Pins. The pins model has the following attributes:
:artist, :year, :title, :rank, :description, and :image
The image uses Paperclip and is stored on Amazon S3 if that matters
I am trying to allow a user to see a review that another user posted and click a link to more simply write their own review for that same album. So basically the link takes them to the Pin.new page and the form already has the :artist, :title, :image, and :year filled in.
I figured out how to do it while bringing ALL of the attributes, including the :description and :rank, which I don't want. I am also not able to bring over the image to the new form.
Here is the pins_controller.rb code I'm using:
def copy
#source = Pin.find(params[:id])
#pin = #source.dup
render 'new'
end
And in my show view:
<%= link_to "copy", copy_pin_path(params[:id]) %>
So question one is how to #source.dup only :artist, :title, and :year
question two is how to bring over the paperclip image. I tried adding this to my pins_controller.rb:
def copy
#source = Pin.find(params[:id])
#pin = #source.dup
#pin.image = #source.image
render 'new'
end
but that didn't work.
UPDATE: miller350 answered my first question, but I still can't get the paperclip image to copy over to the "new" form. In the console I can copy from one Pin record and save a new pin like this:
r = Pin.new
r.image = Pin.find(83).image
r.save
I think the issue is that the new pin form requires that an image is chosen from the user's PC before saving the pin. Whatever that function of Paperclip is, I think that is where I'm getting stuck.
For the first part, you could pull the attributes individually and set them to the instance variable, and for the second part, you could use URI.
def copy
#source = Pin.find(params[:id])
#image = URI.parse(#source.image)
#pin = Pin.new(artist: #source.artist, title: #source.title, year: #source.year, image: #image)
render 'new'
end
Here is what I ended up doing to copy the image to a new pin form.
I have Paperclip's image_remote_url functionality set up already, but I needed to add .to_s to the method below in the pin.rb:
def image_remote_url=(url_value)
self.image = URI.parse(url_value).to_s unless url_value.blank?
super
end
Then my controller:
def copy
#source = Pin.find(params[:id])
#image = #source.image.url
#pin = Pin.new(artist: #source.artist, album: #source.album, year: #source.year, image_remote_url: #image)
render 'new'
end
This pastes the url of the image into the new form. It only works on my Heroku production version, not the local version.
Try below code to copy paperclip image from one record to another
Suppose you have one active record of model Course and copy image to another record
c1 = Course.last
c2.image = c1.image
c2.save
how it works -
c2.image = c1.image - It copy from c1.image to local file
and when you save it then it first delete c2.image if present and set new image to that record

Rolify and Rails 4 role management

I am trying to implement a role based access system in my rails 4 app, and I want the end user (super_admin) to have the ability to edit role assignments via the UI.
I have achieved some success but can't help feeling that there has to be a better way (Since I'm new to rails). Here is my code:
users_roles_controller.rb
# GET /user_roles/new/:id
def new
#roles = Role.all
end
# POST /user_roles/new/:id
def create
populated = params[:roles][:name].reject(&:empty?)
populated.each do |key|
#user.add_role Role.find(key).name
end
redirect_to users_path
end
And in my Form (HAML and simple_form):
= simple_form_for :roles, :url => create_user_roles_path(#user.id), :method => "post" do |f|
= f.input :name, :collection => #roles, as: :check_boxes
= f.button :submit
I'm struggling with the following:
How do I validate form entries, since there is no model?
Should I be using strong parameters and if so how do I implement on a form without a model
How do I create something similar, but with Current roles already checked? (True role management)
UPDATE
I have looked at using the reform Gem as suggested by the comments. This seems like a great solution. I am however having issues with the implementation on this case.
Let me map it out:
I have 3 tables in the database:
users
users_roles (Mapping Table with 2 Attributes : user_id & role_id {Join Table -> HABTM})
roles
I want to construct a form with all the values in the Roles model as checkboxes.The checkboxes should dictate what values are fed into the users_roles table (With relation to a specific user). What I want reform to do is validate the input of this form. This form will always display all of the values in Roles, but some/all of the boxes might be unchecked.
I have created a form folder in my app and started with the following code:
class UserRoleForm < Reform::Form
property :user__id, on: :user
property :role_id, on: :role
validates :user__id, presence: true
validates :role__id, presence: true
end
Am I going in the right direction?
Thanks for the help.
You need two things to build your form: a user's roles and the possible roles.
If I recall correctly, rolify gives your model associations ad should just let you do something like some_user.roles to return all the roles applied to some_user.
To get possible roles, try Role.all.
To combine both, try
user_roles = some_user.roles.pluck(:name) # suppose this returns ["admin"]
Role.pluck(:name).map { |r| [r, user_roles.include?(r)] }
You now have an array like this that you can use to build your form checkboxes with an each loop.
[["customer", false], ["admin", true], ["editor", false]]
Define your reform object's sync or save method to handle what to do with the submitted input, however you are handling it. You can (SHOULD) make a custom validation to verify if the submitted roles are valid roles.