Serve a file from public in a controller action - ruby-on-rails-4

In my project, we have a single-page application, and our build process compiles the application into a single, static file at public/index.html.
The single-page application is responsible for handling all the routing in the app, so no matter whether you visit site.com or site.com/foo/bar/action or site.com/☃, I want the Rails application to serve public/index.html
I have this route:
match '*path', to: 'foo#index', via: :all
And in FooController:
class FooController < ApplicationController
def index
render 'index.html', layout: false
end
end
This does not work as I would hope; I get the error Missing template foo/index.html, application/index.html
Is it possible to serve an assets from public as part of a Rails controller action?

In Rails 5 I got this to work by doing
render file: "public/examplename.html", layout: false

This was way easier than I thought it would be.
I changed:
render 'index.html', layout: false
to
render 'public/index.html', layout: false
and everything worked as expected.

This appears to have changed. I'm trying to temporarily render a static file for the admin portion of my app, which will be a VueJS SPA that talks to rails via an API. I had to do the following:
routes.rb
match '*path', :to => 'console#index', :constraints => { :subdomain=>'console'}, :via=>:all
console_controller.rb
class ConsoleController < ApplicationController
def index
render :file=>'/public/console/index.html', :layout=>false
end
end
So using :file instead of no parameter on the path - because otherwise Rails 5 was trying to treat it like a normal view template.
With that path matching and a subdomain, this seems to work well, as all routes will pass through to the VueJS router.

Related

Routes in Rails SAML IdP

I'm trying to get this gem to work with Rails 4 application that will serve as a SAML identity provider.
The thing that is confusing me is the routes and the template I assume should be rendered. In the gem controller, there is this:
def new
render template: "saml_idp/idp/new"
end
My routes are just the basic setup from the example, which I assume should match the action in my custom controller that inherits from the gem controller.
I have this in my controller.
class SamlIdpController < SamlIdp::IdpController
def idp_authenticate(email, password)
true
end
def idp_make_saml_response(user)
encode_SAMLResponse("you#example.com")
end
end
And my routes.rb file:
get '/saml/auth' => 'saml_idp#new'
get '/saml/metadata' => 'saml_idp#show'
So, what am I missing here? There should be a view rendered, instead I'm getting No Route Matches errors. Thanks.
As per Doc, I think you missed including SamlIdp::IdpController module
please include SamlIdp::IdpController rather than excluding.
Hope, It will work.
The new update for saml_idp gem wants to include SamlIdp::Controller as a module. And the controller class can inherit from ApplicationController
In your case it will be:
class SamlIdpController < ApplicationController
include SamlIdp::Controller
end

generated path does not match custom route?

(Rails 4.2)
I have a miss-match of routes that's in the routes.rb file vs the generated from my page. What is it I am doing wrong?
This is the raked route I want to access :
see_hint_deck_card_tracker GET /decks/:deck_id/cards/:card_id/trackers/:id/see_hint(.:format) trackers#see_hint
I am actually taken to what I think is the correct url, but it tells me I don't have a route for this page:
http://localhost:3000/decks/9/cards/2/trackers/1/see_hint
I have the following routes:
resources :decks do
resources :cards do
resources :trackers do
member do
get 'see_hint'
end
end
end
end
app/controllers/tracker_controller.rb :
class TrackerController < ApplicationController
def show_hint
puts 'we found this'
end
end
inside my /decks/:id/cards/:id/show I have this link_to: (get_tracker, calls for a helper method to get the correct tracker)
<%= link_to "Reveal Hint", see_hint_deck_card_tracker_path(#card.deck, #card, get_tracker), id: "reveal_hint_button" %>
I think your error message is probably telling you you don't have a Controller for that route, not that the Route is missing. This is because you're using the plural resources in your routes.rb, but your controller name is singular:
# Your Code:
resources :trackers
controller TrackerController
# Expected Code:
resources :trackers
controller TrackersController
^^^
You'll also want to make sure your controller is available at app/controllers/trackers_controller.rb (note the plurality).

Ruby on Rails App Testing with Rspec and Capybara

when i create a feature test for my application get the following error:
ActionController::RoutingError:
No route matches [GET] "/example"
My application uses subdomains and sub-applications(engines/modules) within these subdomains. Now when i set for Capybara the app_host or default_host through an feature_subdomain_helper like
Capybara.app_host = "example.lvh.me" or
Capybara.default_host = "example.lvh.me"
and into my rails_helper.rb i add the following code line
config.extend SubdomainHelpers, type: :feature
I get the same error. Now i think the configured subdomain are not considered by my feature test.
My Rspec Version is: 3.2
and Capybara Version is: 2.4.4
My sample feature test looks like:
require 'rails_helper'
feature 'Example Page' do
scenario 'visit example page' do
visit "/example"
expect(page).to have_content 'Example'
end
end
Have someone an idea what i do wrong?
Edit:
Mainapp routes:
constraints(Subdomain) do
mount Example::Engine => '/', as: 'example'
end
Engine routes:
Example::Engine.routes.draw do
scope '/example', nav_scope: 'example' do
end
end
The names of Capybara.default_host and Capybara.app_host are slightly misleading since they both need to be set as URLs to function properly
Capybara.default_host = "http://example.lvh.me"
If that doesn't fix your issue check rake routes and make sure the action you think is mounted at '/example' really is.

Redirecting a request in a routing constraint

I have Sidekiq mounted in my routes file to the /sidekiq endpoint.
I use a constraints option to have it call an external class for validation as a way of preventing non-privelaged users from accessing that endpoint.
# config/routes.rb
mount Sidekiq::Web => "/sidekiq", constraints: Sidekiq::AdminConstraint.new
# lib/sidekiq/admin_constraint.rb
module Sidekiq
class AdminConstraint
def matches?(request)
return false unless request.session[:user_id]
user = User.find_by_id(request.session[:user_id])
user && Ability.new(user).can?(:manage, :sidekiq)
end
end
end
This setup works great. However, it only lets me return true / false on whether the request should go through or not. It does not let me -
Set a flash message (e.g. "You are not permitted to access that page") and
Redirect to some arbitrary page
In that sense, I'm looking for it to behave more like a controller's before_filter.
Is there a way I can modify the request object that's passed in to implement that redirect?
Thanks!
I don't have idea directly set the flash messages, But we can use in different way.
Use the following solution
In your routes.rb, add the following line in the end of the file
match "*path", :to => "application#error_404"
This basically means, any path that is not defined in your route will end up going to error_404 in application_controller. Its very important to put this at the end of your file
And in your ApplicationController, add
def error_404
redirect_to root_path
end
Thanks

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