Prohibit Direct URL for public, Rails - ruby-on-rails

I'm making a Rails application.
When user post content, it makes a folder in public folder. Only admin can put images in it. And user can watch it in folder he made.
But if other users put direct url like this http://railsapp.com/folder/test/test.jpg/, it shows.
I want to prohibit to show when somebody access directly. How should I do?
Do I make the folder in Rails.root? But I don't know how to show it. Please give me a advise.

The solution here is pretty obvious - don't upload to /public if you don't want it to be public. And no you don't want to change the access rules for your public directory as this will mess up all your assets and the rails fallback error pages.
The common choice is to use /storage but then you need to use Rails to serve the assets.
That would involve creating a model and a controller for the uploaded files and serving them with send_file.
class AttachmentController
def show
#attachment = Attachment.find(params[:id])
# #todo authorize access.
send_file #attachment.path, type: #attachment.mime, disposition: 'inline'
end
end
A workaround for cases when you don't want to tie up rails threads just with serving images is to use a non guessable hash in the path name to your "private" assets. Note that they are not really private - just harder to find.

Related

How to preprocess files in the "public" folder in Rails?

I've got a javascript file that I need to be accessed from outside my site (from an external site), but I also want to pre-process the file (ie. with Rail's Asset pipeline) so I can make use of some environment variables in the JS File.
Any idea how to do this? If I put the JS file into the public folder, I cannot pre-process it. However, if I put the file into the assets folder structure I can pre-process it, however, it is then not public.
Please excuse my ignorance, I'm new to this! ;)
Cheers,
Matt.
What you put in your assets folder is indeed public. If it weren't you could not link to javascripts, stylesheets and images there. The only problem is that rails hashes the name of the files for cache busting purposes (only in production).
As a workaround you may create a controller action that gets the hashed file path using the javascript_url helper and redirects to it.
class ThisController < ApplicationController
def some_action
redirect_to ActionController::Base.helpers.javascript_url('javascripts/your_file')
end
end
Maybe try using grunt to build a task that exports the processed javascript file to the public folder and overwrites the old version everytime it builds.
http://developers.mobilesystem7.com/blog/post/replacing-rails-asset-pipeline-with-grunt-bower-browserify/

Managing access to a file with a direct link

I have a rails app where I upload files. I'm using gem cancan for managing access to files. The files are stored on the disk. Is it possible to manage access to a file or restrict/allow it even when a user has a direct link to it? Note I'm not using nginx or apache, it's a local application, therefore at best it's unicorn or simply the standard Rails web server.
Yes, it's possible. Move your files out of public folder, which is served by by your web server, to some other folder (private for example) and use send_file in controller to transmit data. Some pseudo-code for controller:
def get_file
absolute_path = Rails.root + 'private' + params[:filepath]
if current_user.allowed_to_download?(params[:filepath]) && File.exists?(absolute_path)
send_file absolute_path, status: 200 # ok
else
render status: 403 # forbidden
end
end
You will have to setup route for this action, but basicly that's all.

Restrict access to images in Rails app

I have rails project. In my project I load images on server (rails 3 + paperclip + devise + cancan). I want to limit access to files (for example, the original image can be viewed only by the administrator). How should I do it?
If you are limiting by an attribute in your database then one way would be to serve the image via a controller. It's not the most performant but it is secure.
I haven't tried this out, but if you were serving the image from a URL like
/images/picture_of_mickey_mouse.png
then you could create a route in your app that responds to /images with a filename attribute and serve all those images through a controller.
e.g.
class ImagesController < ApplicationController
before_filter :authenticate_admin!, :only => [:show]
def show
send_file "/images/#{params[:filename]}", :disposition => 'inline'
end
end
You would want to make sure you sanitize the params[:filename] however, otherwise the user would be able to download any file on your server!
The docs on send_file are here: http://apidock.com/rails/ActionController/DataStreaming/send_file
Files are handled with ActionDispatch::Static. IMO the best solution is provide similar middleware (basing on Rails source code) but with some authentication and insert it before ActionDispatch::Static. This would work with Warden-based solutions (like Devise) since they do authentication in middleware; just ensure your middleware is put after Warden and old plain ActionDispatch::Static is run just after them.
EDIT: One more thing worth noting. It's quite common in production that Nginx (I'm not sure about Apache and others) is configured to serve all the static files by itself, without passing those requests to rack stack. You may need to disable this Nginx' feature.

How do I add asset search paths to Sprockets based on a wildcard subdomain in rails 3.1?

The Rails Asset Pipeline guide instructs you to use config.assets.paths in config/application.rb but I don't have access to the request's subdomain at this point.
I'd like to be able to prepend an extra path (for the current request only) based on the request's subdomain.
My application specific details
It's a basic CMS app. The root domain.com host handles the administrative part with standard controller/view rendering and default asset paths.
Requests to subdomain.domain.com renders the site based on subdomain. It calls prepend_view_path in a before_filter and adds Rails.root.join('vendor/sites/[subdomain]/templates') for the current request only.
I'd like to be able to prepend Rails.root.join('vendor/sites/[subdomain]/assets') to the Sprockets search paths when the request host is [subdomain].domain.com.
EDIT
I ended up just dropping in a mixin for Sprockets::Environment that overwrites the call method:
module SiteAssetsResolver
def call(env)
begin
# prepend path based on subdomain (from env)
super # Sprockets::Server#call
ensure
# remove path based on subdomain
end
end
end
MyApp::Application.assets.extend(SiteAssetsResolver)
Just as you did for your view path, add a before filter and append the new path to Rails.application.config.assets.paths
I got this idea while watching Railscasts #279 Understanding the Asset Pipeline
I agree with commenter on your question that said "The asset pipeline isn't really meant to be compiling your assets each request in production." -- making it not really possible to do exactly what you ask.
So how about an alternative to accomplish what you're really trying to accomplish here, which is different asset resolution for different subdomains. Put your sub-domain specific assets in sub-directories of your asset folders.
Now, in the view/helpers, when you call asset_path or any other helpers that take a relative asset path, ask it for "#{subdomain}/name_of_asset" instead of just "name_of_asset".
Now, because of the way the asset compiler works, it's possible this subdirectory method won't work, you may have to put the subdomain at the beginning of the actual filename instead. "#{subdomain}_name_of_asset". Not sure.
And this still wouldn't give you a sort of 'default fall through' where some assets in some subdomains don't have subdomain-specific assets, they just 'fall through' to the default. Which would be nice. It's possible a way can be figured out to do that too, not sure.
But at any rate, following this approach of asking for a different asset at display-time using logic in view/helper.... is going to get you further than your original suggested approach, which probably isn't possible.

Rails serving files per user

Hey. In my rails application each client has a file. This file must not be accessible to public, only to the user (authenticated user). How would you implement this functionality?
You need put this file in other place than /public directory.
After you can use send_file in your controller to send file by user.

Resources