Linking a model to a specific user - RESTful Authentication


#1

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


#2

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 G. 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


#3

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.


#4

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.


#5

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.


#6

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 G. 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.


#7

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.


#8

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


#9

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


#10

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.


#11

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).


#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.


#13

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.


#14

Hi Cisco,

I will post my solution later tonight. I’m at work so I don’t have
access.
Awesome, thanks a bunch.


#15

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


#16

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

#17

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?


#18

Thanks a bunch everyone, it’s working beautifully.