Rails 3 Password Protect Single View - ruby-on-rails-3

I have gone through and looked at a bunch of screen casts (http://railscasts.com/episodes/270-authentication-in-rails-3-1) and tutorials (http://ruby.railstutorial.org/chapters/modeling-and-viewing-users-two?version=2.3#sec:secure_passwords) but I can't seem to find or apply what I am learning to create a single view password protected.
As of now I am generating a random password and showing it to the user to go back and view a file they have uploaded (generated using SecureRandom.hex). However, I know that using http_basic doesn't seem to work when I restrict it only to the show view and method in my controller : http_basic_authenticate_with :password => :passcode, :only => :show
I know that line of code does not work because :passcode does not reference the individual files password that was created.
Also, I know that http_basic_authenticate_with is not the way it should be done for secure passwords, so how would you go about this using has_secure_password etc?

You can try to encapsulate the http_basic_authenticate_with :password => :passcode in a method in your controller, and call the before_filter to call that method.
Something like that:
before_filter :restrict, :only => :show
def show
end
def restrict
http_basic_authenticate_with :password => :passcode
end

Related

Devise routing, problems customising Devise routes

After customising Devise routes, I have some issues with the routing.
Currently setup (but failing):
/me/account loads Devise::Registration#edit form
/me/account/:what routes to account_controller#edit
My routes (shortcut):
devise_for :users do
...
end
devise_scope :user do
scope "/me/account" do
get "/" => "users/registrations#edit", :as => :my_account
get "/:what" => "accounts#edit", :as => :my_account_edit
end
end
resources :accounts, :only => [:edit, :update]
Rake routes output:
activate_account GET /reactivate(.:format) users#reactivate
my_account GET /me/account(.:format) users/registrations#edit
my_account_edit GET /me/account/:what(.:format) accounts#edit
cancel GET /me/account/cancel(.:format) users/registrations#cancel
DELETE /me/account(.:format) users/registrations#destroy
edit_account GET /accounts/:id/edit(.:format) accounts#edit
account PATCH /accounts/:id(.:format) accounts#update
PUT /accounts/:id(.:format) accounts#update
Account
Since /me/account is actually showing registrations#edit ( Devise ) and all the /me/account/helpme are custom form fields
This has issues:
No notices shown on /me/account on update or failure
On failure the form is not repopulated with earlier filled in form values
Its not updating the form
/me/account/helpme goes , on form submit , to /accounts/1 ( the current user id ) and throws error
No route matches {:action=>"edit", :controller=>"accounts", :id=>"1", :what=>nil} missing required keys: [:what]
These issues are totally driving me insane. Anyone can provide me some suggestions to fix (one or more ) of these routing issues?
About form submit error.
You need to override url in your form to submit to:
<%= form_for #resource, url: my_account_edit(what: params[:what]) do |f| %>
This should be done in your views or in devise-generated views.
If you didn't generated devise views, then, just run in terminal:
rails g devise:views
EDIT
And you should tell us how your form in views looks like, and how controller handles updates of custom fields.

How can you make sure a username won't conflict with an existing route?

So I'd like to have urls on my site like http://foobar.com/hadees that goes to someone's profile. However when registering usernames how do I make sure they don't pick something that will conflict with my existing routes?
I'm guessing I need to get a list of the existing routes but I'm not sure how to do it.
A short google search gives me that:
http://henrik.nyh.se/2008/10/validating-slugs-against-existing-routes-in-rails
In rails 3 the method has moved to Rails.application.routes.recognize_path
So I summarize :
class User < ActiveRecord::Base
validates_format_of :name, :with => /\A[\w-]+\Z/
validates_uniqueness_of :name
validate :name_is_not_a_route
protected
def name_is_not_a_route
path = Rails.application.routes.recognize_path("/#{name}", :method => :get) rescue nil
errors.add(:name, "conflicts with existing path (/#{name})") if path && !path[:username]
end
end
Good question. Through a little tinkering, I found that you can get the routes in your app via:
Rails.application.routes.routes.collect{|r| r.path}

rails:3 Devise signup Filter chain halted as :require_no_authentication rendered or redirected

I am using Devise in my site I create admin namespace and giving functionality of create user by admin.
my routes are as under
devise_for :users,:has_many => :comments, :controllers => {:sessions =>'devise/sessions',:registrations => 'devise/registrations'} do
get "/login", :to => "devise/sessions#new", :as => :login
get "/signup", :to => "devise/registrations#new", :as => :signup
get "/logout", :to => "devise/sessions#destroy", :as => :logout
end
when i click on add_user link which has signup_path causing following error:
My Error
Started GET "/signup" for 127.0.0.1 at Fri Mar 09 12:49:11 +0530 2012
Processing by Devise::RegistrationsController#new as HTML
User Load (0.1ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 35 LIMIT 1
Redirected to http://localhost:3000/admin
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 3ms (ActiveRecord: 0.1ms)
I think it going to the devise registration controller but due to following line it cause an error in devise registration controller
prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ]
The mentioned line on Devise's Controller makes sense in general cases: a logged in user can't sign up. As you're on a case where only an admin can create a user, I would suggest that you don't use Devise's controller on Registerable module and write your own controller with your own rules. You can write it based on Devise's controller changing only the mentioned line.
If you want to use it, try skipping the before_filter with skip_before_filter. I don't think it is the best solution. and I would write my own controller.
I was receiving the following error in my development log.
Filter chain halted as :require_no_authentication
An endless loop was created because devise's after_sign_in_path_for in application_controller.rb was redirecting back to the previous page with
stored_location_for(resource)
Devise's gem signs in the user when the password is edited.
Like you, I wanted an Admin user to be able to add new users. But I didn't want to mess with the Devise Registerable, since I actually wanted users to still be able to register themselves. I have some admin users with permission to add a new user, so I created additional methods in my users controller and additional views to handle that case.
My additional methods are not referenced by Devise's prepend_before_filter :require_no_authentication, so they don't get the error.
My recipe:
In app/controllers/users_controller.rb (or whatever object you are using devise for):
Copy the new, create and update methods and rename the copies to admin_new, admin_create, and admin_update.
In app/views/users, copy new.html.erb to admin_new.html.erb Change the partial to refer to admin_form instead of form:
<%= render 'admin_form' %>
Then copy the partial _form.html.erb to _admin_form.html.erb. In _admin_form.html.erb, change the form_for to use a different URL:
form_for(#user, :url => '/users/admin_create')
Add routes in config/routes.rb to point to the alternate methods in the user controller:
devise_scope :user do
...
get 'users/admin_new' => 'users#admin_new'
post 'users/admin_create' => 'users#admin_create'
end
Now you can add users while you are logged in by going to /users/admin_new, and users are still able to create their own user (register) using the devise mechanism that you have not disturbed.
I got an even simpler solution:
prepend_before_filter :require_no_authentication, only: [:cancel ]
By removing :new, :create from the prepend_before_filter it overrides devise source code and solve the problem. I had the same issue and it solved my problem.
I also noticed in my application that when I create a user and log in as that user, the above messages are displayed by the rails server console and I am redirected to rails localhost default window 'Yay, You are on Rails'.
This is because no page is defined to take the logged-in user. When I defined a welcome page 'index.html.erb' in a folder, say, 'welcome' under 'views' folder, the login was successful and it worked. You can try this.

Rails 3 Routing: Using 2 dynamic segments in path for one model

What I am trying to achieve is something similar to Github's way for routes. E.g. I have a project with the name 'question' results in the URL /hjuskewycz/question. So my goal is to have routes where the first segment is the username and the second the project's name.
I tried a couple of different approaches, this is the one I am stuck with right now:
scope ":username" do
resources :projects, :path => "" do
resources :pictures
end
end
Using
project_path :username => project.owner.username, :id => project.to_param
works as expected. However, it's tedious to always specify the username although it's always the owner's username. I would very much prefer
project_path(:id => project.to_param)
I know about default_url_options and url_for and I digged in the code. However, polymorphic_url doesn't use default_url_options.
I tried in routes.rb:
resources :projects, :path => "", :defaults => {:username => Proc.new { "just_testing" }}
since you can use a proc for constrains, but haven't got it working either.
I tried in project.rb
def to_param
"#{owner.username"/#{project.title}"
end
I spent already too much time on this problem and my current approach uses a convenience method to add the :username parameter. Nevertheless, I think using this method all over the place just to add an entry stinks (bad code smell). I wonder if there is a more elegant solution to this problem.
I think you should not make things complicated here, just use something like this:
In Routes.rb
match ':username/:projectname/' => 'projects#show_project' , :as => :show_project
and in project_controller, just define this
def show_project
#user =User.find_by_username(params[:username])
#project =Project.find_by_slug(params[:projectname])
end
Simpler is better, it saves time and easy to understand for others
You want to do something like this in your controller:
before_filter :set_username
def set_username
Rails.application.routes.default_url_options[:username] = #user.name
end

Does anybody have any tips for managing polymorphic nested resources in Rails 3?

In config/routes.rb:
resources :posts do
resources :comments
end
resources :pictures do
resources :comments
end
I would like to allow for more things to be commented on as well.
I'm currently using mongoid (mongomapper isn't as compatible with Rails 3 yet as I would like), and comments are an embedded resource (mongoid can't yet handle polymorphic relational resources), which means that I do need the parent resource in order to find the comment.
Are there any elegant ways to handle some of the following problems:
In my controller, I need to find the parent before finding the comment:
if params[:post_id]
parent = Post.find(params[:post_id]
else if params[:picture_id]
parent = Picture.find(params[:picture_id]
end
which is going to get messy if I start adding more things to be commentable.
Also url_for([comment.parent, comment]) doesn't work, so I'm going to have to define something in my Comment model, but I think I'm also going to need to define an index route in the Comment model as well as potentially an edit and new route definition.
There might be more issues that I have to deal with as I get further.
I can't imagine I'm the first person to try and solve this problem, are there any solutions out there to make this more manageable?
I had to do something similar in an app of mine. I took what I came up with and changed it around a bit, but I haven't tested it, so use with care. It's not pretty, but it's better than anything else I was able to think of.
In routes.rb:
resources :posts, :pictures
controller :comments do
get '*path/edit' => :edit, :as => :edit_comment
get '*path' => :show, :as => :comment
# etc. The order of these is important. If #show came first, it would direct /edit to #show and simply tack on '/edit' to the path param.
end
In comment.rb:
embedded_in :commentable, :inverse_of => :comments
def to_param
[commentable.class.to_s.downcase.pluralize, commentable.id, 'comments', id].join '/'
end
In a before filter in comments_controller.rb:
parent_type, parent_id, scrap, id = params[:path].split '/'
# Security: Make sure people can't just pass in whatever models they feel like
raise "Uh-oh!" unless %w(posts pictures).include? parent_type
#parent = parent_type.singularize.capitalize.constantize.find(parent_id)
#comment = #parent.comments.find(id)
Ok, ugliness over. Now you can add comments to whatever models you want, and simply do:
edit_comment_path #comment
url_for #comment
redirect_to #comment
And so on.
Edit: I didn't implement any other paths in my own app, because all I needed was edit and update, but I'd imagine they'd look something like:
controller :comments do
get '*path/edit' => :edit, :as => :edit_comment
get '*path' => :show, :as => :comment
put '*path' => :update
delete '*path' => :destroy
end
The other actions will be trickier. You'll probably need to do something like:
get ':parent_type/:parent_id/comments' => :index, :as => :comments
post ':parent_type/:parent_id/comments' => :create
get ':parent_type/:parent_id/comments/new' => :new, :as => :new_comment
You'd then access the parent model in the controller using params[:parent_type] and params[:parent_id]. You'd also need to pass the proper parameters to the url helpers:
comments_path('pictures', 7)
Ryan Bates covered polymorphic associations in Railscasts #154, but the example was for Rails 2 and Active Record. I managed to get his example working using Rails 3 and Mongoid by making a few changes.
In the Post and Picture models, add the following line:
embeds_many :comments, :as => :commentable
According to the Mongoid associations documentation, all embedded_in associations are polymorphic. You don't need the commentable_id and commentable_type columns mentioned in the Railscast when using Mongoid, because the comment is a child of the commentable. In the Comment model, add the following line:
embedded_in :commentable, :inverse_of => :comment
Setup the routes in config/routes.rb like this:
resources posts do
resources comments
end
resources pictures do
resources comments
end
Add the following method to your comments controller as a private method. This is identical to Ryan's method:
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
In each of your comments controller actions where you need to find the comment, call the find_commentable method first to get the parent. Once the parent has been found, you can find the comment by ID, by searching through the commentable's comments. For example, in the edit action the code to find the comment would look like this:
#commentable = find_commentable
#comment = #commentable.comments.find(params[:id])
To reduce the repetition of calling find_commentable at the start of every action, you could put a before filter at the top of the controller like this:
class CommentsController < ApplicationController
before_filter :find_commentable
...
And then change the return call in the find_commentable method to:
return #commentable = $1.classify.constantize.find(value)
I haven't encountered any problems using this method, but if you come across any issues please point them out.
Drawing on Uriptical's answer, I found the relationships to work but named routes still did not.
I'm still pretty new at rails 3 but I found a simple solution using eval.
For instance, in my project, the polymorphic parents (represented in my app as the mongoid objects Product and Category) are defined as #imagable using a modification of find_comentable and the child being edited is referred to as #image.
a url such as product_image_path(#imagable, #image) which does
GET => products/:product_id/images/ can be replaced with:
send("#{#imagable.class.name.downcase}_image_url", #imagable, image )
This works for all named paths. For instance:
link_to 'Edit', send("edit_#{#imagable.class.name.downcase}_image_path", #imagable, image )
link_to 'Destroy', send("#{#imagable.class.name.downcase}_image_url", #imagable, image), :confirm => 'Are you sure?', :method => :delete
The downside to this is it leaves sends all over your views and in controllers wherever you have redirects.
Is there a more elegant solution to do this via routes?
*replaced eval with send