Rails Way to Restrict Page Access Without Sessions - ruby-on-rails

Context
I'm building a super simple, knock-your-socks-off sexy sign-up page at http://hivechatter.com. (Yes, I feel strongly about her.)
The root page is the new user action, where I ask for email only. If the visitor submits a valid email, a new user is created and I redirect to that user's edit page and ask for additional, optional info.
Problem
The edit page url is of the usual form: http://hivechatter.com/users/19/edit. One can visit any user's edit page by simply visiting this url with whichever id number they choose.
Question
How do I restrict access to the edit user page so that it can only be visited once, and only immediately after having created that user_id from the root new user page?
I can think of a variety of methods to explore. I'd appreciate a pointer on the most elegant, rails way to do this. Note that I don't need any additional functionality like sessions, etc. This two step sign-up process is the extent of what I need right now.
Thanks!

Add new column to your users table. Let it be opened_once:boolean with DEFAULT false
Then in your users_controller
def edit
#user = User.find( params[:id], :conditions => ['opened_once => ?', false] ) rescue ActiveRecord::RecordNotFound
#user.update_attribute :opened_once, true
...
end
so now it can be showed only once right after creating new user when you redirect to edit page
UPD
What you can do more Rails way? Without adding new stuff to your database and so on. You can remove your edit action at all, so your edit view will rendered at create:
def create
#user = User.new params[:user]
respond_to do |format|
if #user.save
format.html{ render :action => :edit }
else
format.html{ render :action => :new }
end
end
end
User will see edit form only once if validation passed and his profile created.
So this is specific "Rails way" :)

The point of a cookie is to maintain state in the form of a session. HTTP by spec is stateless, and there for if you have people logging in then they need a session. RoR has a great session handler, I recommend using it.
The only other way to restrict access would be using a .htaccess file or similar method of doing basic-auth. This doesn't scale well and is less secure.

Related

How to look check whether model exists and create if it doesn't with a single form?

I'm trying to implement a form with the following behavior:
1) Input some semantic data about a user (i.e. username).
2) Do a User.find_by(:username = username).
3) If such a user exists, direct to show page for that user.
4) If such a user does not exist, create a new user with the provided username, then redirect to the show page for that user.
This should be simple but I cannot figure out how to format the form_for helper and my show and create actions in my user_controller to implement this behavior.
I currently have:
form_with :url => 'users/:id', :method => :get do
...
end
because I'm ultimately trying to invoke the "show" method of the controller. However, my form does not take in a user's id as a parameter, and when the user does not yet exist there is no :id parameter to access at the time of the form's submission.
How can I set up my form to redirect to show in each case, while still adhering to the logic explained above?
You can do something like this in your User's Controller create action
def create
usr_name = params[:username]
#user = User.where(username: usr_name).first_or_initialize
if #user.persisted?
redirect_to user_path(#user) # or whatever your user show path is
elsif #user.save
redirect_to user_path(#user)
else
render :new
end
end
You would just need to make sure that you are validating the uniqueness of usernames.
Also, first_or_initialize(and its counterpart first_or_create) can take in a block. So, you can assign other attributes to the new User like this...
User.where(username: usr_name).first_or_initialize do |usr|
usr.some_attribute = some_value
end
you can use find_or_initialize_by(unique_key) in your create method.unique_key can be any key which you are using to identify your user such as email,phone etc.

Can I override just part of an ActiveAdmin controller action using `super` + custom redirect?

I've registered a Widget in ActiveAdmin and want to change the redirect that takes place after creating a new one. So that I can accomplish various things with Javascript, I've created a custom form for creating/editing them such that in /admin/widget.rb I have this:
form do |f|
render "create_or_edit_widget"
end
I want to modify the basic Admin::WidgetsController#create action to change where the user is redirected after successfully creating one. I can fill out the rest of the custom action to complete this, except I don't know how to handle a case where the .save fails and the user is redirected back to the form with the formtastic inline error messages. I know how I could do this if I wanted the normal Rails form behavior of creating a list of error messages but not enough about Formtastic to copy its behavior. So far I have this:
controller do
def create
#widget = Widget.new(params[:widget])
if #widget.save
redirect_to admin_widgets_path, notice: "Successfully created Widget."
else
redirect_to :back
end
end
end
I was wondering if I can somehow user super and then only change the redirect path after successful creation instead of having to write out the entire action. If that's not possible, can anyone tell me where in the ActiveAdmin GitHub I'd be able to find the standard #create action so I can copy it out and change the one part?
Yes, you can do that. Here is a working code from my application using super and just changing the redirection
def create
super do |format|
redirect_to admin_submission_discussion_path(id: resource.discussion.slug, submission_id: resource.discussion.client_application.slug) and return if resource.valid?
end
end

Redirect from custom devise edit page

I am using devise for user registrations. I set up a custom edit page '/info' to use as an additional signup page. The only problem is after you submit the edits on the info page it redirects to the user profile. I am able to change it redirect to the home page (where I want after /info) but then it also redirects there from the normal edit page. I am not sure how to redirect based on what page the user is on.
My one idea was to get the users current path and use an if statement but I haven't been able to get that to work.
registrations_controller.rb:
def update
...
current_uri = request.env['PATH_INFO']
if current_uri == '/info'
redirect_to root_path, notice: "Welcome"
else
redirect_to user_path(#user)
end
end
Normally it just looks like this:
def update
...
redirect_to user_path(#user)
end
The problem seems to be you need to keep data around to determine where to redirect. A few ideas:
You could store data in the session when a user visits a previous page and check this data in your action to determine where to redirect the user. The session is often a convenient place to store things that are consistent between requests but that should not be persisted to the database.
You could store data in the database (for instance, add a flag to your user model) and check this to determine where to redirect.
You could check request.referer and redirect based on the value there. This may not be as reliable.

How to route RESTfully with multiple entry points?

I have a model called Project, which is a collection of information stored by a Company. This company can create projects two ways.
The first is the standard RESTful way - the company goes to the Project index, then clicks 'New Project', and upon creation is returned to the index with a flash message.
The second is a 'quick create' that can be accessed when a company is looking at a client's profile. From here, the company can enter some basic information and send this off to create a project with that client (the client is specified automatically here).
The second of these two scenarios has a project being accessed from clients/show. Sending this data to projects/create would ordinarily route the company to projects/index, but I don't want that. In this case, the create action is meaningfully different in that certain fields are treated differently, and the redirect is also different. What would you suggest I do?
Build an alternative 'create_from_client' action in projects.
Build a 'create_project' action in clients.
Send a parameter to projects/create and set client_id and redirect to client/show if that parameter exists.
Something else I'm not aware of.
Thanks!
You can leverage the referrer directly from the Request object and fork based on that, similar to how redirect_to :back works.
From the Rails API docs for the redirect_to options hash:
:back - Back to the page that issued the request.
Useful for forms that are triggered from multiple places.
Short-hand for redirect_to(request.env["HTTP_REFERER"])
So you can simply do something like this:
def create
#project = Project.new( params[:project] )
#project.save
respond_with #project, location: get_location!
end
private
def get_location!
case request.env["HTTP_REFERER"]
# Your routing logic here.
end
This is nice and easy to unit test, too, if you're into that. :)
context "if the user came from the regular form" do
before { controller.request.env["HTTP_REFERER"] = "regular_form_url" }
it "redirects to the index path" do
post :create
response.should redirect_to :index
end
end
context "if the user came from the quick-create form" do
before { controller.request.env["HTTP_REFERER"] = "quick_create_url" }
it "redirects to some other path" do
post :create
response.should redirect_to some_other_path
end
end
I would just add another action to the controller, 'quick_create' or whatever. You can dry out the form with partials and parameters to the partial to tell how to render things...This just seems like the easiest way.
I've got this semi-rational (or is that semi-irrational) hang up against leveraging the referrer...
I ussualy add hidden referer field with current URL then redirect to it
For example
def create
#project = Project.new params[:project]
#project.save
respond_with #project, :location => params[:referer] || [:index, :projects]
end

Create a new user with new openid login authlogic

I've implemented authlogic in a rails site, and I'm trying to get openid to work correctly. So far, you can login just fine as long as you have an existing account, but not so much if you don't. I'd like to be able to automagically create a new account if the identity_url is not already in the database.
The problem is that I also need to store some additional info. if the user is logging in for the first time with their openid, I'd like to ask them to fill in basic info (name, email), BEFORE the account is created.
I've played around with a few methods, but nothing seems to be working.
Thanks in advance for any input!
acts_as_authentic do |c|
c.openid_required_fields = [:email,"http://axschema.org/contact/email"]
end
Will allow you to require an email. I'm unsure of how to require other fields, but maybe check that axschema.org page. There is no need for the user to fill anything out other than their OpenID provider URL.
Combining login and registration could be done with something like this (untested create method from UserSessions controller, like from the authlogic tutorial stuff)
def create
if User.find_by_openid_provider(params[:user_session]).nil? # or something like that
#user = User.new(params[:user_session])
if #user.save
redirect_to whatever_path
else
# handle error
end
else
#user_session = UserSession.new(params[:user_session])
if #user_session.save
add_message 'Login successful!'
redirect_to whatever_path
else
render :action => :new
end
end
end
Maybe try putting the additional information into a temp table of some kind, and keep track of the session the user is in. Once they have authenticated, connect the previously entered information with the OpenID information to create the real user.

Resources