Forum: Ruby on Rails Linking a model to a specific user - RESTful Authentication

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
1c86ce2ff3d15dc1af340075e245dc63?d=identicon&s=25 Vince Gilbert (vfgilber)
on 2009-03-24 03:07
Hi,

I am having a really tough time figuring this out. I followed the
tutorial below to add a RESTful authentication to a Ruby application
that tracks projects (just a title and a url).  The tutorial is for a
blog, but I just changed blog to projects

 http://ruby.about.com/od/rubyonrails/ss/railsblog3.htm

My main table of projects is:

projects
-------
ID: integer
Title: string
Url: string

The RESTful Authentication plugin adds:

user
------
ID: integer
login: varchar
password:varchar
....

and a sessions controller.

I would like the application to show the user a list of the projects
that belong to them when they go do the projects index action.  However,
I have no idea how to link the user ID to a particular project and then
list their projects based on whether they are the appropriatly logged in
user.

I figure that when a new project is created, the create method could add
the user ID to the project in another column.  And then, when the
list/show action is called for the projects, only the appropriate
projects will show.

Here is the projects controller.  Can anyone help me with this?  I'm in
over my head.  Thanks, Vince.

class ProjectsController < ApplicationController
  before_filter :login_required

  # GET /projects
  # GET /projects.xml
  def index
@projects = Project.find(:all)
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @projects }
    end
  end

  # GET /projects/1
  # GET /projects/1.xml
  def show
@project = Project.find(params[:id])


    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @project }
    end
  end

  # GET /projects/new
  # GET /projects/new.xml
  def new
    @project = Project.new
     respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @project }

    end
  end

  # GET /projects/1/edit
  def edit
    @project = Project.find(params[:id])
  end

  # POST /projects
  # POST /projects.xml
  def create
    @project = Project.new(params[:project])
       #@project.clientID = @session['user'].id
    respond_to do |format|
      if @project.save

        flash[:notice] = 'Project was successfully created.'
        format.html { redirect_to(@project) }
        format.xml  { render :xml => @project, :status => :created,
:location => @project }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @project.errors, :status =>
:unprocessable_entity }
      end
    end
  end

  # PUT /projects/1
  # PUT /projects/1.xml
  def update
    @project = Project.find(params[:id])

    respond_to do |format|
      if @project.update_attributes(params[:project])
        flash[:notice] = 'Project was successfully updated.'
        format.html { redirect_to(@project) }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @project.errors, :status =>
:unprocessable_entity }
      end
    end
  end

  # DELETE /projects/1
  # DELETE /projects/1.xml
  def destroy
    @project = Project.find(params[:id])
    @project.destroy

    respond_to do |format|
      format.html { redirect_to(projects_url) }
      format.xml  { head :ok }
    end
  end
end
1c86ce2ff3d15dc1af340075e245dc63?d=identicon&s=25 Vince Gilbert (vfgilber)
on 2009-03-24 03:10
I thought I would include the sessions controller too, just in case it
helps:

# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
  # Be sure to include AuthenticationSystem in Application Controller
instead


  # render new.rhtml
  def new
  end

 # render logout.rhtml
  def logout
  end

  def create
    self.current_user = User.authenticate(params[:login],
params[:password])
    if logged_in?
      if params[:remember_me] == "1"
        current_user.remember_me unless current_user.remember_token?
        cookies[:auth_token] = { :value =>
self.current_user.remember_token , :expires =>
self.current_user.remember_token_expires_at }
      end
      redirect_back_or_default('/')
      flash[:notice] = "Logged in successfully"
    else
      render :action => 'new'
    end
  end

  def destroy
    self.current_user.forget_me if logged_in?
    cookies.delete :auth_token
    reset_session
    flash[:notice] = "You have been logged out."
    redirect_back_or_default('/')
  end
end


Vince Gilbert wrote:
> Hi,
>
> I am having a really tough time figuring this out. I followed the
> tutorial below to add a RESTful authentication to a Ruby application
> that tracks projects (just a title and a url).  The tutorial is for a
> blog, but I just changed blog to projects
>
>  http://ruby.about.com/od/rubyonrails/ss/railsblog3.htm
>
> My main table of projects is:
>
> projects
> -------
> ID: integer
> Title: string
> Url: string
>
> The RESTful Authentication plugin adds:
>
> user
> ------
> ID: integer
> login: varchar
> password:varchar
> ....
>
> and a sessions controller.
>
> I would like the application to show the user a list of the projects
> that belong to them when they go do the projects index action.  However,
> I have no idea how to link the user ID to a particular project and then
> list their projects based on whether they are the appropriatly logged in
> user.
>
> I figure that when a new project is created, the create method could add
> the user ID to the project in another column.  And then, when the
> list/show action is called for the projects, only the appropriate
> projects will show.
>
> Here is the projects controller.  Can anyone help me with this?  I'm in
> over my head.  Thanks, Vince.
>
> class ProjectsController < ApplicationController
>   before_filter :login_required
>
>   # GET /projects
>   # GET /projects.xml
>   def index
> @projects = Project.find(:all)
>     respond_to do |format|
>       format.html # index.html.erb
>       format.xml  { render :xml => @projects }
>     end
>   end
>
>   # GET /projects/1
>   # GET /projects/1.xml
>   def show
> @project = Project.find(params[:id])
>
>
>     respond_to do |format|
>       format.html # show.html.erb
>       format.xml  { render :xml => @project }
>     end
>   end
>
>   # GET /projects/new
>   # GET /projects/new.xml
>   def new
>     @project = Project.new
>      respond_to do |format|
>       format.html # new.html.erb
>       format.xml  { render :xml => @project }
>
>     end
>   end
>
>   # GET /projects/1/edit
>   def edit
>     @project = Project.find(params[:id])
>   end
>
>   # POST /projects
>   # POST /projects.xml
>   def create
>     @project = Project.new(params[:project])
>        #@project.clientID = @session['user'].id
>     respond_to do |format|
>       if @project.save
>
>         flash[:notice] = 'Project was successfully created.'
>         format.html { redirect_to(@project) }
>         format.xml  { render :xml => @project, :status => :created,
> :location => @project }
>       else
>         format.html { render :action => "new" }
>         format.xml  { render :xml => @project.errors, :status =>
> :unprocessable_entity }
>       end
>     end
>   end
>
>   # PUT /projects/1
>   # PUT /projects/1.xml
>   def update
>     @project = Project.find(params[:id])
>
>     respond_to do |format|
>       if @project.update_attributes(params[:project])
>         flash[:notice] = 'Project was successfully updated.'
>         format.html { redirect_to(@project) }
>         format.xml  { head :ok }
>       else
>         format.html { render :action => "edit" }
>         format.xml  { render :xml => @project.errors, :status =>
> :unprocessable_entity }
>       end
>     end
>   end
>
>   # DELETE /projects/1
>   # DELETE /projects/1.xml
>   def destroy
>     @project = Project.find(params[:id])
>     @project.destroy
>
>     respond_to do |format|
>       format.html { redirect_to(projects_url) }
>       format.xml  { head :ok }
>     end
>   end
> end
Eb6042d3fdf039e9259b1a5f17db337c?d=identicon&s=25 The Ultimation (pimeamark)
on 2009-03-24 21:56
Make a field in your project model called user_id. Then in the
Project.rb file put

belongs_to :user

And in the User model

has_many :projects

Then when you create a project, set user_id = current_user.id. Finally
to display them simply use user.projects

Hope this helped.
1c86ce2ff3d15dc1af340075e245dc63?d=identicon&s=25 Vince Gilbert (vfgilber)
on 2009-03-25 02:27
Thank you very much for the help.  I am starting to build a schema in my
brain as to how some of this works.

I have a small problem with the last instruction.  How do I use the
"user.projects" in the index method below:

Thank you again - much appreciated.

  # GET /posts
  # GET /posts.xml
  def index
  @posts = Post.find(:all, :order => 'created_at DESC')
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @posts }
    end
  end


Mark Mr wrote:
>Finally
> to display them simply use user.projects
>
> Hope this helped.
1c86ce2ff3d15dc1af340075e245dc63?d=identicon&s=25 Vince Gilbert (vfgilber)
on 2009-03-25 03:58
OK - I managed to figure it out myself using:

user_id = current_user.id
@posts = Post.find_all_by_user_id(user_id)

That works great for the index method.

Where I'm having trouble now is the Show method.  As it stands, clicking
on the title of one of the projects calls the Show method like

http://localhost:3000/projects/1

The problem is, if the user types manually into the address bar, they
could call up whichever project id they want.

Can someone give me a hand with the show method.  I'm thinking that an
If statement checking to see if the project.user_id for the project.id
in the parameter match.  If not, redirect.  If so, then go ahead and
show the project record.  I'm not sure of syntax:

def show
user_id = current_user.id
If user_id = :id then
    @post = Post.find(params[:id])
    respond_to do |format|
        format.html # show.html.erb
        format.xml  { render :xml => @post }
    end
Else
    redirect_back_or_default('/projects')
end if
end



Vince Gilbert wrote:
> Thank you very much for the help.  I am starting to build a schema in my
> brain as to how some of this works.
>
> I have a small problem with the last instruction.  How do I use the
> "user.projects" in the index method below:
>
> Thank you again - much appreciated.
>
>   # GET /posts
>   # GET /posts.xml
>   def index
>   @posts = Post.find(:all, :order => 'created_at DESC')
>     respond_to do |format|
>       format.html # index.html.erb
>       format.xml  { render :xml => @posts }
>     end
>   end
>
>
> Mark Mr wrote:
>>Finally
>> to display them simply use user.projects
>>
>> Hope this helped.
Eb6042d3fdf039e9259b1a5f17db337c?d=identicon&s=25 The Ultimation (pimeamark)
on 2009-03-25 14:58
I'd recommend trying out the restful ACL plugin.
http://www.railslodge.com/plugins/966-restful-acl

This is an effective way to set permissions for your project and is good
to use in general. In this case, you could simply put in your Post
model:

  def is_readable_by(user, parent = nil)
    self.user.eql?(user)
  end

This example is assuming you have belongs_to :user in your Post model.
It takes a little while to learn but once you do it should be easy. I
put this in all my models.
1c86ce2ff3d15dc1af340075e245dc63?d=identicon&s=25 Vince Gilbert (vfgilber)
on 2009-03-25 15:47
Hello Mark,  thank you for your suggestion.  I gather that the RESTful
ACL plugin is meant to be used instead of RESTful Authentication Plugin
that I am currently using?

Vince

Mark Mr wrote:
> I'd recommend trying out the restful ACL plugin.
> http://www.railslodge.com/plugins/966-restful-acl
>
> This is an effective way to set permissions for your project and is good
> to use in general. In this case, you could simply put in your Post
> model:
>
>   def is_readable_by(user, parent = nil)
>     self.user.eql?(user)
>   end
>
> This example is assuming you have belongs_to :user in your Post model.
> It takes a little while to learn but once you do it should be easy. I
> put this in all my models.
Eb6042d3fdf039e9259b1a5f17db337c?d=identicon&s=25 The Ultimation (pimeamark)
on 2009-03-25 17:21
No you should use both. Restful authentication creates users and ACL
tells the program what parts of the website those users (or non-users)
can access. The ACL is really simple, it's just a matter of putting
those ACL functions in each model.
408016ab4c062f421c418946185aa232?d=identicon&s=25 Cisco Ri (ciscor)
on 2009-04-23 14:47
I am creating a similar application using restful authentication.  Here
is my schema -- do I need to add a user_id column to my user table?

  create_table "links", :force => true do |t|
    t.string   "url"
    t.string   "title"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "name",                      :limit => 100, :default =>
""
    t.string   "email",                     :limit => 100
    t.string   "crypted_password",          :limit => 40
    t.string   "salt",                      :limit => 40
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "remember_token",            :limit => 40
    t.datetime "remember_token_expires_at"
  end
408016ab4c062f421c418946185aa232?d=identicon&s=25 Cisco Ri (ciscor)
on 2009-04-23 14:47
I am creating a similar application using restful authentication.  Here
is my schema -- do I need to add a user_id column to my user table?

  create_table "links", :force => true do |t|
    t.string   "url"
    t.string   "title"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "name",                      :limit => 100, :default =>
""
    t.string   "email",                     :limit => 100
    t.string   "crypted_password",          :limit => 40
    t.string   "salt",                      :limit => 40
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "remember_token",            :limit => 40
    t.datetime "remember_token_expires_at"
  end
D188e591eac11021329b8821a5f954c7?d=identicon&s=25 Ar Chron (railsdog)
on 2009-04-23 19:08
Umm... no.

Table 'users' already has an implicit 'id' field (you don't have to
mention them in your migrations), just like your 'links' table does. If
you are sticking to the rails standard, you needn't declare them, they
are the rails default primary key for their respective tables.

In your 'links' table, a 'user_id' field tells rails that:

a) this field, 'user_id', contains an id to a record in another table -
i.e., this record "belongs to" that record in that table over there,

and that

b) the related table is 'users' (field name - '_id', pluralized).
408016ab4c062f421c418946185aa232?d=identicon&s=25 Cisco Ri (ciscor)
on 2009-04-23 20:52
Ar Chron wrote:
> Umm... no.
>
> Table 'users' already has an implicit 'id' field (you don't have to
> mention them in your migrations), just like your 'links' table does. If
> you are sticking to the rails standard, you needn't declare them, they
> are the rails default primary key for their respective tables.
>
> In your 'links' table, a 'user_id' field tells rails that:
>
> a) this field, 'user_id', contains an id to a record in another table -
> i.e., this record "belongs to" that record in that table over there,
>
> and that
>
> b) the related table is 'users' (field name - '_id', pluralized).

Thanks for letting me know.  In links/new, how would I go about
including the current user_id?  The only way I know of would be a hidden
form element, and I would like to keep it all server side.
1c86ce2ff3d15dc1af340075e245dc63?d=identicon&s=25 Vince Gilbert (vfgilber)
on 2009-04-23 21:12
Cisco Ri wrote:
> Ar Chron wrote:
>> Umm... no.
>>
>> Table 'users' already has an implicit 'id' field (you don't have to
>> mention them in your migrations), just like your 'links' table does. If
>> you are sticking to the rails standard, you needn't declare them, they
>> are the rails default primary key for their respective tables.
>>
>> In your 'links' table, a 'user_id' field tells rails that:
>>
>> a) this field, 'user_id', contains an id to a record in another table -
>> i.e., this record "belongs to" that record in that table over there,
>>
>> and that
>>
>> b) the related table is 'users' (field name - '_id', pluralized).
>
> Thanks for letting me know.  In links/new, how would I go about
> including the current user_id?  The only way I know of would be a hidden
> form element, and I would like to keep it all server side.

Hi Cisco,

I will post my solution later tonight.  I'm at work so I don't have
access.
408016ab4c062f421c418946185aa232?d=identicon&s=25 Cisco Ri (ciscor)
on 2009-04-23 21:19
> Hi Cisco,
>
> I will post my solution later tonight.  I'm at work so I don't have
> access.
Awesome, thanks a bunch.
D188e591eac11021329b8821a5f954c7?d=identicon&s=25 Ar Chron (railsdog)
on 2009-04-23 22:08
Cisco Ri wrote:
>
> Thanks for letting me know.  In links/new, how would I go about
> including the current user_id?  The only way I know of would be a hidden
> form element, and I would like to keep it all server side.

Restful Authentication has the notion of current_user, does it not?
Check in the authenticated_system.rb file in lib for a peek at some
methods you have available to you.

There's no need to know  the current_user's id in the links controller's
new action, that just needs to manufacture an @link for the new.html.erb
form.

You will need it for the create action in the links controller though.
Something like:

def create
  @link = Link.new(params[:link])
  if logged_in?
    @link.user_id = current_user.id
    if @link.save
      redirect_to(@link)
    else
      render :action => 'new'
    end
  else
    # redirect to your login page?
    # I'd actually defend this method with a before_filter, and get
    # rid of this if logged_in? stuff
  end
end
065cc6140c6a1320fa2aa87eaa98a2ed?d=identicon&s=25 Aaron Brown (Guest)
on 2009-04-23 22:48
(Received via mailing list)
Ar Chron wrote:
> There's no need to know  the current_user's id in the links controller's
> new action, that just needs to manufacture an @link for the new.html.erb
> form.
>
> You will need it for the create action in the links controller though.
> Something like:

You can do it a little more simply with the "build" helper, provided
there's a has/belongs_to relationship between the user and the link.

@link = current_user.links.build(params[:link])

...will automatically set the user_id field by virtue of the
association.

 - Aaron
408016ab4c062f421c418946185aa232?d=identicon&s=25 Cisco Ri (ciscor)
on 2009-04-24 09:29
Thanks a bunch everyone, it's working beautifully.
408016ab4c062f421c418946185aa232?d=identicon&s=25 Cisco Ri (ciscor)
on 2009-04-24 11:04
Cisco Ri wrote:
> Thanks a bunch everyone, it's working beautifully.
One more question: how would I submit a new link using a GET request?
This topic is locked and can not be replied to.