I''m trying to create a simple authentication system but am failing miserably. I''m (sort of) following the "Logging In" chapter of the Agile book. Ideally, any access to http://example.com/publish (and any of its subdirectories) should redirect to http:/example.com/publish/login when there is no valid session user_id. Code so far: -------------------------------------------- # controller class PublishController < ApplicationController before_filter :authenticate, :except => [:login, :login_user] def login if request.post? logged_in_user = @user.attempt_login if logged_in_user session[:user_id] = logged_in_user.id redirect_to(:action => ''index'') else flash[:notice] = ''Invalid username or password. Please try again'' end end end def self.authenticate unless session[:user_id] redirect_to :action => ''login'' end end end #model class User < ActiveRecord::Base require "digest/sha1" attr_accessor :password attr_accessible :username, :password, :first_name, :last_name, :email validates_uniqueness_of :username validates_presence_of :username, :first_name, :last_name, :email validates_length_of :password, :within => 5..40, :on => :create def before_create self.sha_password = User.encrypt(self.password) end def after_create @password = nil end private def self.encrypt(password) Digest::SHA1.hexdigest(password) end def self.login_user(username, password) encrypted_password = encrypt(password || "") find(:first, :conditions => ["username = ? and sha_password = ?", username, encrypted_password]) end def attempt_login User.login_user(self.username, self.password) end end -------------------------------------------- If I browse to, say, http://example.com/publish/user/list I get a NoMethodError in Publish#user: undefined method `authenticate'' for #<PublishController:0x387ab58>W When I browse to http://example.com/publish/login/ I see the login form but upon submit I get another NoMethodError in Publish#login: You have a nil object when you didn''t expect it! The error occured while evaluating nil.attempt_login I''m totally stumped and appreciate any help. Greg -------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060302/089667f2/attachment-0001.html
Greg MacGregor wrote:> I''m trying to create a simple authentication system but am failing > miserably. I''m (sort of) following the "Logging In" chapter of the Agile > book. Ideally, any access to http://example.com/publish (and any of its > subdirectories) should redirect to http:/example.com/publish/login when > there is no valid session user_id.Greg MacGregor wrote:> I''m trying to create a simple authentication system but am failing > miserably. I''m (sort of) following the "Logging In" chapter of the Agile > book. Ideally, any access to http://example.com/publish (and any of its > subdirectories) should redirect to http:/example.com/publish/login when > there is no valid session user_id.Are you sure you don''t want to save a lot of time and use one of the pre-written packages that are available? Salted, for example: http://wiki.rubyonrails.com/rails/pages/SaltedHashLoginGenerator I am a fellow Rails newbie, and I had no problem getting Salted up and running. And it''s really easy to modify. You might also want to look here, for user *authorization*: http://d-haven.org/modules/news/article.php?storyid=28 I haven''t tried that yet, but I''m going to set it up tomorrow, and I''m thinking it will be up and running in 1-2 hours. One of the reasons Rails is so awesome is that there is SO much existing code out there to use. And it''s so easy to modify to do exactly what you want to do. BUT... if you do want to write your own User system, I''m sure there''s a lot of folks here who can help you. You wouldn''t want to be taking Rails or Ruby coding advice from me just yet... Micah -- Posted via http://www.ruby-forum.com/.
Micah Bly wrote:> Are you sure you don''t want to save a lot of time and use one of the > pre-written packages that are available?... building it myself (and running into errors such as this) will help me to better understand how the controller and model interact with one another. I appreciate the links, Micah! Greg -- Posted via http://www.ruby-forum.com/.
On 3/1/06, Greg MacGregor <gmacgregor@gmail.com> wrote:> > I''m trying to create a simple authentication system but am failing > miserably. I''m (sort of) following the "Logging In" chapter of the Agile > book. Ideally, any access to http://example.com/publish (and any of its > subdirectories) should redirect to http:/example.com/publish/login when > there is no valid session user_id. > > Code so far: > > -------------------------------------------- > > # controller > class PublishController < ApplicationController > > before_filter :authenticate, :except => [:login, :login_user] > > def login > if request.post? > logged_in_user = @user.attempt_login > if logged_in_user > session[:user_id] = logged_in_user.id > redirect_to(:action => ''index'') > else > flash[:notice] = ''Invalid username or password. Please try again'' > end > end > end > > def self.authenticateThis needs to be a regular method (what''s the correct terminology here?), not a class method. You want: def authenticate And I''d put the authenticate method in the Application controller, that way other controllers can access it (note: if you do that, you''ll need to change the below redirect to also include the controller. Joe> unless session[:user_id] > redirect_to :action => ''login'' > end > end > > end > > # model > class User < ActiveRecord::Base > > require "digest/sha1" > > attr_accessor :password > attr_accessible :username, :password, :first_name, :last_name, :email > > validates_uniqueness_of :username > validates_presence_of :username, :first_name, :last_name, :email > validates_length_of :password, :within => 5..40, :on => :create > > def before_create > self.sha_password = User.encrypt(self.password ) > end > > def after_create > @password = nil > end > > private > def self.encrypt(password) > Digest::SHA1.hexdigest(password) > end > > def self.login_user(username, password) > encrypted_password = encrypt(password || "") > find(:first, :conditions => ["username = ? and sha_password = ?", > username, encrypted_password]) > end > > def attempt_login > User.login_user(self.username, self.password) > end > > end > > -------------------------------------------- > > If I browse to, say, http://example.com/publish/user/list I > get a NoMethodError in Publish#user: > undefined method `authenticate'' for #<PublishController:0x387ab58>W > When I browse to http://example.com/publish/login/ I see > the login form but upon submit I get another NoMethodError in Publish#login: > You have a nil object when you didn''t expect it! > The error occured while evaluating nil.attempt_login > > > I''m totally stumped and appreciate any help. > > Greg > > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails > > >
Hi Greg, comments inline: On 1-Mar-06, at 8:37 PM, Greg MacGregor wrote:> > I''m trying to create a simple authentication system but am failing > miserably. I''m (sort of) following the "Logging In" chapter of the > Agile book. Ideally, any access to http://example.com/publish (and > any of its subdirectories) should redirect to http:/example.com/ > publish/login when there is no valid session user_id. >Okay, it''s the "sort of" that''s biting you in the rear right now. I''ll point out why you''re getting the errors but I advise you to go back and work through the Logging In chapter again, step by step. more below:> Code so far: > > -------------------------------------------- > > # controller > class PublishController < ApplicationController > > before_filter :authenticate, :except => [:login, :login_user] > > def login > if request.post? > logged_in_user = @user.attempt_login > if logged_in_user > session[:user_id] = logged_in_user.id > redirect_to(:action => ''index'') > else > flash[:notice] = ''Invalid username or password. Please > try again'' > end > end > end >One of your errors is that you''re trying to call (effectively) "nil.attempt_login". The offending line is above, where you call @user.attempt_login. The problem is, you''re not assigning a value to @user so it''s ''nil''. If I look at your User model below, I''d guess you really want to say: logged_in_user = User.login_user(params[:username], params[:password]) of course, this assumes you''re actually passing username and password as POST params.> def self.authenticate > unless session[:user_id] > redirect_to :action => ''login'' > end > end >The authenticate method above has "self." in front of it which makes it a *class* method. i.e. you can only call it as PublishController.authenticate However, your filters are expected to be *instance* methods of the controller so ditch the ''self.'' and that will eliminate your "undefined method `authenticate`" error. HTH, Trevor -- Trevor Squires http://somethinglearned.com> > end > > # model > class User < ActiveRecord::Base > > require "digest/sha1" > > attr_accessor :password > > attr_accessible :username, :password, :first_name, :last_name, :email > > validates_uniqueness_of :username > validates_presence_of :username, :first_name, :last_name, :email > validates_length_of :password, :within => 5..40, :on => :create > > def before_create > self.sha_password = User.encrypt(self.password ) > end > > def after_create > @password = nil > end > > private > def self.encrypt(password) > Digest::SHA1.hexdigest(password) > end > > def self.login_user(username, password) > encrypted_password = encrypt(password || "") > find(:first, :conditions => ["username = ? and sha_password > = ?", username, encrypted_password]) > end > > def attempt_login > User.login_user(self.username, self.password) > end > > end > > -------------------------------------------- > > If I browse to, say, http://example.com/publish/user/list I get a > NoMethodError in Publish#user: > undefined method `authenticate'' for #W > When I browse to http://example.com/publish/login/ I see the login > form but upon submit I get another NoMethodError in Publish#login: > You have a nil object when you didn''t expect it! > The error occured while evaluating nil.attempt_login > > I''m totally stumped and appreciate any help. > > Greg > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060302/2711033d/attachment.html
Thanks Trevor. Before you posted I made these changes: class PublishController < ApplicationController before_filter :authenticate def login if request.post? @user = User.new(params[:user]) logged_in_user = @user.attempt_login if logged_in_user session[:user_id] = logged_in_user.id redirect_to(:controller => ''/publish/user'', :action => ''list'') else flash[:notice] = ''Invalid username or password. Please try again'' end end end def authenticate unless session[:user_id] redirect_to :action => ''login'' end end end ---- and i added this to my model: public def attempt_login User.login_user(self.username, self.password) end (note public) everything seems to work now but I have another problem. After successful login I want to point the user to example.com/publish/user/list. I''ve referred to this as redirect_to(:controller => ''/publish/user'', :action => ''list'') above. My app looks like app +controllers -application_controller.rb -publish_controller.rb +publish -user_controller.rb There is indeed a list action in the user controller, so how come the redirect doesn''t work? Thanks again for the help. Greg -- Posted via http://www.ruby-forum.com/.
On 3/1/06, Greg <gmacgregor@gmail.com> wrote:> Thanks Trevor. Before you posted I made these changes: > > class PublishController < ApplicationController > > before_filter :authenticate > > def login > if request.post? > @user = User.new(params[:user]) > logged_in_user = @user.attempt_login > if logged_in_user > session[:user_id] = logged_in_user.id > redirect_to(:controller => ''/publish/user'', :action => ''list'') > else > flash[:notice] = ''Invalid username or password. Please try > again'' > end > end > end > > def authenticate > unless session[:user_id] > redirect_to :action => ''login'' > end > end > > end > > > ---- > > and i added this to my model: > > public > def attempt_login > User.login_user(self.username, self.password) > end > > (note public) > > everything seems to work now but I have another problem. After > successful login I want to point the user to > example.com/publish/user/list. I''ve referred to this as > > redirect_to(:controller => ''/publish/user'', :action => ''list'') > > above. My app looks like > > app > +controllers > -application_controller.rb > -publish_controller.rb > +publish > -user_controller.rb > > There is indeed a list action in the user controller, so how come the > redirect doesn''t work? > > Thanks again for the help. > > GregIs the User controller defined like: class Publish::AdminController < ApplicationController ? What exactly doesn''t work with the redirection?
Greg, Like you, when I first started with Rails I used the authentication sample from the Agile book. But I quickly dicovered that it was too limited, even for the hobby site I was building. I recommend looking at the acts_as_authenticated plugin. It''s easy to set up, and has convenience methods for handy things like displaying the name of the user logged on (e.g. current_user.login). Or, "logged_in?", a simple test to determine if someone is logged in. This is handy if you want to display special menus to users who are logged in vs. the general public. Of course, that just scratches the surface. It also integrates ActionMailer for emailing, etc. Of course, you''re free to code this stuff yourself, but I decided that using an available plugin for the drudgery of authentication left me with more time for writing my "actual" application. FYI, There''s even a separate, new ACL system written to complement the acts_as_authenticated plugin (I haven''t tried it yet). So you can assign roles/permissions to certain users. Good luck! -Chris -- Posted via http://www.ruby-forum.com/.
Joe Van Dyk wrote:> Is the User controller defined like: > class Publish::AdminController < ApplicationController > ? > > What exactly doesn''t work with the redirection?Given my structure above, the User controller is defined like this: class Publish::UserController < ActionController ... or -app +controllers -publish_controller.rb +publish -user_controller.rb Like I said, there is a list action in the user_controller that I know works. After login I am redirected to: http://example.com/publish/user/list and get the error: "no action for user#list" or something to that effect. I don''t understand why that is happening if the action is indeed there?! I think that I may use a pre-fab authentication controller after all. This is giving me a headache. Thanks again to all of you for the help. Greg -- Posted via http://www.ruby-forum.com/.