Browse files

initial commit of the restful_authentication plugin

git-svn-id: http://svn.techno-weenie.net/projects/plugins/restful_authentication@1477 567b1171-46fb-0310-a4c9-b4bef9110e78
  • Loading branch information...
0 parents commit 2366199ce5bb056565611dcfbe25f6a78930493e technoweenie committed Aug 1, 2006
24 README
@@ -0,0 +1,24 @@
+Restful Authentication Generator
+====
+
+This is a basic restful authentication generator for rails, taken from acts as authenticated. Currently it requires Rails 1.2 (or edge).
+
+To use:
+
+ ./script/generate authenticated user sessions
+
+The first parameter specifies the model that gets created in signup (typically a user or account model). A model with migration is created,
+as well as a basic controller with the create method.
+
+The second parameter specifies the sessions controller name. This is the controller that handles the actual login/logout function on the site.
+
+You can pass --skip_migration to skip the user migration.
+
+From here, you will need to add the resource routes in config/routes.rb.
+
+ map.resources :users, :sessions
+
+Generate your mailer:
+
+ ./script/generate authenticated_mailer user
+
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the restful_authentication plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the restful_authentication plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'RestfulAuthentication'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
1 generators/authenticated/USAGE
@@ -0,0 +1 @@
+./script/generate authenticated USERMODEL CONTROLLERNAME
157 generators/authenticated/authenticated_generator.rb
@@ -0,0 +1,157 @@
+class AuthenticatedGenerator < Rails::Generator::NamedBase
+ attr_reader :controller_name,
+ :controller_class_path,
+ :controller_file_path,
+ :controller_class_nesting,
+ :controller_class_nesting_depth,
+ :controller_class_name,
+ :controller_singular_name,
+ :controller_plural_name
+ alias_method :controller_file_name, :controller_singular_name
+ alias_method :controller_table_name, :controller_plural_name
+ attr_reader :model_controller_name,
+ :model_controller_class_path,
+ :model_controller_file_path,
+ :model_controller_class_nesting,
+ :model_controller_class_nesting_depth,
+ :model_controller_class_name,
+ :model_controller_singular_name,
+ :model_controller_plural_name
+ alias_method :model_controller_file_name, :model_controller_singular_name
+ alias_method :model_controller_table_name, :model_controller_plural_name
+
+ def initialize(runtime_args, runtime_options = {})
+ super
+
+ @controller_name = args.shift
+ @model_controller_name = @name.pluralize
+
+ # sessions controller
+ base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
+ @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
+
+ if @controller_class_nesting.empty?
+ @controller_class_name = @controller_class_name_without_nesting
+ else
+ @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
+ end
+
+ # model controller
+ base_name, @model_controller_class_path, @model_controller_file_path, @model_controller_class_nesting, @model_controller_class_nesting_depth = extract_modules(@model_controller_name)
+ @model_controller_class_name_without_nesting, @model_controller_singular_name, @model_controller_plural_name = inflect_names(base_name)
+
+ if @model_controller_class_nesting.empty?
+ @model_controller_class_name = @model_controller_class_name_without_nesting
+ else
+ @model_controller_class_name = "#{@model_controller_class_nesting}::#{@model_controller_class_name_without_nesting}"
+ end
+ end
+
+ def manifest
+ recorded_session = record do |m|
+ # Check for class naming collisions.
+ m.class_collisions controller_class_path, "#{controller_class_name}Controller", # Sessions Controller
+ "#{controller_class_name}Helper"
+ m.class_collisions model_controller_class_path, "#{model_controller_class_name}Controller", # Model Controller
+ "#{model_controller_class_name}Helper"
+ m.class_collisions class_path, "#{class_name}"
+ m.class_collisions [], 'AuthenticatedSystem', 'AuthenticatedTestHelper'
+
+ # Controller, helper, views, and test directories.
+ m.directory File.join('app/models', class_path)
+ m.directory File.join('app/controllers', controller_class_path)
+ m.directory File.join('app/controllers', model_controller_class_path)
+ m.directory File.join('app/helpers', controller_class_path)
+ m.directory File.join('app/views', controller_class_path, controller_file_name)
+ m.directory File.join('test/functional', controller_class_path)
+ m.directory File.join('app/controllers', model_controller_class_path)
+ m.directory File.join('app/helpers', model_controller_class_path)
+ m.directory File.join('app/views', model_controller_class_path, model_controller_file_name)
+ m.directory File.join('test/functional', model_controller_class_path)
+ m.directory File.join('test/unit', class_path)
+
+ m.template 'model.rb',
+ File.join('app/models',
+ class_path,
+ "#{file_name}.rb")
+
+ m.template 'controller.rb',
+ File.join('app/controllers',
+ controller_class_path,
+ "#{controller_file_name}_controller.rb")
+
+ m.template 'model_controller.rb',
+ File.join('app/controllers',
+ model_controller_class_path,
+ "#{model_controller_file_name}_controller.rb")
+
+ m.template 'authenticated_system.rb',
+ File.join('lib', 'authenticated_system.rb')
+
+ m.template 'authenticated_test_helper.rb',
+ File.join('lib', 'authenticated_test_helper.rb')
+
+ m.template 'functional_test.rb',
+ File.join('test/functional',
+ controller_class_path,
+ "#{controller_file_name}_controller_test.rb")
+
+ m.template 'model_functional_test.rb',
+ File.join('test/functional',
+ model_controller_class_path,
+ "#{model_controller_file_name}_controller_test.rb")
+
+ m.template 'helper.rb',
+ File.join('app/helpers',
+ controller_class_path,
+ "#{controller_file_name}_helper.rb")
+
+ m.template 'model_helper.rb',
+ File.join('app/helpers',
+ model_controller_class_path,
+ "#{model_controller_file_name}_helper.rb")
+
+ m.template 'unit_test.rb',
+ File.join('test/unit',
+ class_path,
+ "#{file_name}_test.rb")
+
+ m.template 'fixtures.yml',
+ File.join('test/fixtures',
+ "#{table_name}.yml")
+
+ # Controller templates
+ m.template 'login.rhtml', File.join('app/views', controller_class_path, controller_file_name, "new.rhtml")
+ m.template 'signup.rhtml', File.join('app/views', model_controller_class_path, model_controller_file_name, "new.rhtml")
+
+ unless options[:skip_migration]
+ m.migration_template 'migration.rb', 'db/migrate', :assigns => {
+ :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
+ }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
+ end
+ end
+
+ puts
+ puts ("-" * 70)
+ puts "Don't forget the restful routes in config.routes.rb"
+ puts
+ puts " map.resources :#{model_controller_file_name}, :#{controller_file_name}"
+ puts
+ puts "Try these for some familiar login URLs if you like:"
+ puts
+ puts " map.signup '/signup', :controller => '#{model_controller_file_name}', :action => 'new'"
+ puts " map.login '/login', :controller => '#{controller_file_name}', :action => 'new'"
+ puts " map.logout '/logout', :controller => '#{controller_file_name}', :action => 'destroy'"
+ puts
+ puts ("-" * 70)
+ puts
+
+ recorded_session
+ end
+
+ protected
+ # Override with your own usage banner.
+ def banner
+ "Usage: #{$0} authenticated ModelName ControllerName"
+ end
+end
131 generators/authenticated/templates/authenticated_system.rb
@@ -0,0 +1,131 @@
+module AuthenticatedSystem
+ protected
+ # Returns true or false if the user is logged in.
+ # Preloads @current_<%= file_name %> with the user model if they're logged in.
+ def logged_in?
+ (@current_<%= file_name %> ||= session[:<%= file_name %>] ? <%= class_name %>.find_by_id(session[:<%= file_name %>]) : :false).is_a?(<%= class_name %>)
+ end
+
+ # Accesses the current <%= file_name %> from the session.
+ def current_<%= file_name %>
+ @current_<%= file_name %> if logged_in?
+ end
+
+ # Store the given <%= file_name %> in the session.
+ def current_<%= file_name %>=(new_<%= file_name %>)
+ session[:<%= file_name %>] = new_<%= file_name %>.nil? ? nil : new_<%= file_name %>.id
+ @current_<%= file_name %> = new_<%= file_name %>
+ end
+
+ # Check if the <%= file_name %> is authorized.
+ #
+ # Override this method in your controllers if you want to restrict access
+ # to only a few actions or if you want to check if the <%= file_name %>
+ # has the correct rights.
+ #
+ # Example:
+ #
+ # # only allow nonbobs
+ # def authorize?
+ # current_<%= file_name %>.login != "bob"
+ # end
+ def authorized?
+ true
+ end
+
+ # Filter method to enforce a login requirement.
+ #
+ # To require logins for all actions, use this in your controllers:
+ #
+ # before_filter :login_required
+ #
+ # To require logins for specific actions, use this in your controllers:
+ #
+ # before_filter :login_required, :only => [ :edit, :update ]
+ #
+ # To skip this in a subclassed controller:
+ #
+ # skip_before_filter :login_required
+ #
+ def login_required
+ username, passwd = get_auth_data
+ self.current_<%= file_name %> ||= <%= class_name %>.authenticate(username, passwd) || :false if username && passwd
+ logged_in? && authorized? ? true : access_denied
+ end
+
+ # Redirect as appropriate when an access request fails.
+ #
+ # The default action is to redirect to the login screen.
+ #
+ # Override this method in your controllers if you want to have special
+ # behavior in case the <%= file_name %> is not authorized
+ # to access the requested action. For example, a popup window might
+ # simply close itself.
+ def access_denied
+ respond_to do |accepts|
+ accepts.html do
+ store_location
+ redirect_to :controller => '<%= controller_file_name %>', :action => 'login'
+ end
+ accepts.xml do
+ headers["Status"] = "Unauthorized"
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
+ render :text => "Could't authenticate you", :status => '401 Unauthorized'
+ end
+ end
+ false
+ end
+
+ # Store the URI of the current request in the session.
+ #
+ # We can return to this location by calling #redirect_back_or_default.
+ def store_location
+ session[:return_to] = request.request_uri
+ end
+
+ # Redirect to the URI stored by the most recent store_location call or
+ # to the passed default.
+ def redirect_back_or_default(default)
+ session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default)
+ session[:return_to] = nil
+ end
+
+ # Inclusion hook to make #current_<%= file_name %> and #logged_in?
+ # available as ActionView helper methods.
+ def self.included(base)
+ base.send :helper_method, :current_<%= file_name %>, :logged_in?
+ end
+
+ # When called with before_filter :login_from_cookie will check for an :auth_token
+ # cookie and log the user back in if apropriate
+ def login_from_cookie
+ return unless cookies[:auth_token] && !logged_in?
+ user = <%= class_name %>.find_by_remember_token(cookies[:auth_token])
+ if user && user.remember_token?
+ user.remember_me
+ self.current_<%= file_name %> = user
+ cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
+ flash[:notice] = "Logged in successfully"
+ end
+ end
+
+ private
+ # gets BASIC auth info
+ def get_auth_data
+ user, pass = nil, nil
+ # extract authorisation credentials
+ if request.env.has_key? 'X-HTTP_AUTHORIZATION'
+ # try to get it where mod_rewrite might have put it
+ authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split
+ elsif request.env.has_key? 'HTTP_AUTHORIZATION'
+ # this is the regular location
+ authdata = request.env['HTTP_AUTHORIZATION'].to_s.split
+ end
+
+ # at the moment we only support basic authentication
+ if authdata && authdata[0] == 'Basic'
+ user, pass = Base64.decode64(authdata[1]).split(':')[0..1]
+ end
+ return [user, pass]
+ end
+end
113 generators/authenticated/templates/authenticated_test_helper.rb
@@ -0,0 +1,113 @@
+module AuthenticatedTestHelper
+ # Sets the current <%= file_name %> in the session from the <%= file_name %> fixtures.
+ def login_as(<%= file_name %>)
+ @request.session[:<%= file_name %>] = <%= file_name %> ? <%= table_name %>(<%= file_name %>).id : nil
+ end
+
+ def content_type(type)
+ @request.env['Content-Type'] = type
+ end
+
+ def accept(accept)
+ @request.env["HTTP_ACCEPT"] = accept
+ end
+
+ def authorize_as(user)
+ if user
+ @request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{users(user).login}:test")}"
+ accept 'application/xml'
+ content_type 'application/xml'
+ else
+ @request.env["HTTP_AUTHORIZATION"] = nil
+ accept nil
+ content_type nil
+ end
+ end
+
+ # http://project.ioni.st/post/217#post-217
+ #
+ # def test_new_publication
+ # assert_difference(Publication, :count) do
+ # post :create, :publication => {...}
+ # # ...
+ # end
+ # end
+ #
+ def assert_difference(object, method = nil, difference = 1)
+ initial_value = object.send(method)
+ yield
+ assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
+ end
+
+ def assert_no_difference(object, method, &block)
+ assert_difference object, method, 0, &block
+ end
+
+ # Assert the block redirects to the login
+ #
+ # assert_requires_login(:bob) { |c| c.get :edit, :id => 1 }
+ #
+ def assert_requires_login(login = nil)
+ yield HttpLoginProxy.new(self, login)
+ end
+
+ def assert_http_authentication_required(login = nil)
+ yield XmlLoginProxy.new(self, login)
+ end
+
+ def reset!(*instance_vars)
+ instance_vars = [:controller, :request, :response] unless instance_vars.any?
+ instance_vars.collect! { |v| "@#{v}".to_sym }
+ instance_vars.each do |var|
+ instance_variable_set(var, instance_variable_get(var).class.new)
+ end
+ end
+end
+
+class BaseLoginProxy
+ attr_reader :controller
+ attr_reader :options
+ def initialize(controller, login)
+ @controller = controller
+ @login = login
+ end
+
+ private
+ def authenticated
+ raise NotImplementedError
+ end
+
+ def check
+ raise NotImplementedError
+ end
+
+ def method_missing(method, *args)
+ @controller.reset!
+ authenticate
+ @controller.send(method, *args)
+ check
+ end
+end
+
+class HttpLoginProxy < BaseLoginProxy
+ protected
+ def authenticate
+ @controller.login_as @login if @login
+ end
+
+ def check
+ @controller.assert_redirected_to :controller => 'account', :action => 'login'
+ end
+end
+
+class XmlLoginProxy < BaseLoginProxy
+ protected
+ def authenticate
+ @controller.accept 'application/xml'
+ @controller.authorize_as @login if @login
+ end
+
+ def check
+ @controller.assert_response 401
+ end
+end
33 generators/authenticated/templates/controller.rb
@@ -0,0 +1,33 @@
+# This controller handles the login/logout function of the site.
+class <%= controller_class_name %>Controller < ApplicationController
+ # Be sure to include AuthenticationSystem in Application Controller instead
+ include AuthenticatedSystem
+ # If you want "remember me" functionality, add the this before_filter uncommended to Application Controller
+ before_filter :login_from_cookie
+
+ # render new.rhtml
+ def new
+ end
+
+ def create
+ self.current_<%= file_name %> = <%= class_name %>.authenticate(params[:login], params[:password])
+ if logged_in?
+ if params[:remember_me] == "1"
+ self.current_<%= file_name %>.remember_me
+ cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
+ end
+ redirect_back_or_default('/')
+ flash[:notice] = "Logged in successfully"
+ else
+ render :action => 'new'
+ end
+ end
+
+ def destroy
+ self.current_<%= file_name %>.forget_me if logged_in?
+ cookies.delete :auth_token
+ reset_session
+ flash[:notice] = "You have been logged out."
+ redirect_back_or_default('/')
+ end
+end
14 generators/authenticated/templates/fixtures.yml
@@ -0,0 +1,14 @@
+quentin:
+ id: 1
+ login: quentin
+ email: quentin@example.com
+ salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
+ crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
+ created_at: <%%= 5.days.ago.to_s :db %>
+aaron:
+ id: 2
+ login: aaron
+ email: aaron@example.com
+ salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
+ crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
+ created_at: <%%= 1.days.ago.to_s :db %>
85 generators/authenticated/templates/functional_test.rb
@@ -0,0 +1,85 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require '<%= controller_file_name %>_controller'
+
+# Re-raise errors caught by the controller.
+class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
+
+class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
+ # Then, you can remove it from this and the units test.
+ include AuthenticatedTestHelper
+
+ fixtures :<%= table_name %>
+
+ def setup
+ @controller = <%= controller_class_name %>Controller.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_should_login_and_redirect
+ post :create, :login => 'quentin', :password => 'test'
+ assert session[:<%= file_name %>]
+ assert_response :redirect
+ end
+
+ def test_should_fail_login_and_not_redirect
+ post :create, :login => 'quentin', :password => 'bad password'
+ assert_nil session[:<%= file_name %>]
+ assert_response :success
+ end
+
+ def test_should_logout
+ login_as :quentin
+ get :destroy
+ assert_nil session[:<%= file_name %>]
+ assert_response :redirect
+ end
+
+ def test_should_remember_me
+ post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
+ assert_not_nil @response.cookies["auth_token"]
+ end
+
+ def test_should_not_remember_me
+ post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
+ assert_nil @response.cookies["auth_token"]
+ end
+
+ def test_should_delete_token_on_logout
+ login_as :quentin
+ get :destroy
+ assert_equal @response.cookies["auth_token"], []
+ end
+
+ def test_should_login_with_cookie
+ <%= table_name %>(:quentin).remember_me
+ @request.cookies["auth_token"] = cookie_for(:quentin)
+ get :new
+ assert @controller.send(:logged_in?)
+ end
+
+ def test_should_fail_cookie_login
+ <%= table_name %>(:quentin).remember_me
+ users(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago.utc
+ @request.cookies["auth_token"] = cookie_for(:quentin)
+ get :new
+ assert !@controller.send(:logged_in?)
+ end
+
+ def test_should_fail_cookie_login
+ <%= table_name %>(:quentin).remember_me
+ @request.cookies["auth_token"] = auth_token('invalid_auth_token')
+ get :new
+ assert !@controller.send(:logged_in?)
+ end
+
+ protected
+ def auth_token(token)
+ CGI::Cookie.new('name' => 'auth_token', 'value' => token)
+ end
+
+ def cookie_for(<%= file_name %>)
+ auth_token <%= table_name %>(<%= file_name %>).remember_token
+ end
+end
2 generators/authenticated/templates/helper.rb
@@ -0,0 +1,2 @@
+module <%= controller_class_name %>Helper
+end
14 generators/authenticated/templates/login.rhtml
@@ -0,0 +1,14 @@
+<%%= start_form_tag <%= controller_plural_name %>_path %>
+<p><label for="login">Login</label><br/>
+<%%= text_field_tag 'login' %></p>
+
+<p><label for="password">Password</label><br/>
+<%%= password_field_tag 'password' %></p>
+
+<!-- Uncomment this if you want this functionality
+<p><label for="remember_me">Remember me:</label>
+<%%= check_box_tag 'remember_me' %></p>
+-->
+
+<p><%%= submit_tag 'Log in' %></p>
+<%%= end_form_tag %>
18 generators/authenticated/templates/migration.rb
@@ -0,0 +1,18 @@
+class <%= migration_name %> < ActiveRecord::Migration
+ def self.up
+ create_table "<%= table_name %>", :force => true do |t|
+ t.column :login, :string
+ t.column :email, :string
+ t.column :crypted_password, :string, :limit => 40
+ t.column :salt, :string, :limit => 40
+ t.column :created_at, :datetime
+ t.column :updated_at, :datetime
+ t.column :remember_token, :string
+ t.column :remember_token_expires_at, :datetime
+ end
+ end
+
+ def self.down
+ drop_table "<%= table_name %>"
+ end
+end
64 generators/authenticated/templates/model.rb
@@ -0,0 +1,64 @@
+require 'digest/sha1'
+class <%= class_name %> < ActiveRecord::Base
+ # Virtual attribute for the unencrypted password
+ attr_accessor :password
+
+ validates_presence_of :login, :email
+ validates_presence_of :password, :if => :password_required?
+ validates_presence_of :password_confirmation, :if => :password_required?
+ validates_length_of :password, :within => 4..40, :if => :password_required?
+ validates_confirmation_of :password, :if => :password_required?
+ validates_length_of :login, :within => 3..40
+ validates_length_of :email, :within => 3..100
+ validates_uniqueness_of :login, :email, :case_sensitive => false
+ before_save :encrypt_password
+
+ # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
+ def self.authenticate(login, password)
+ u = find_by_login(login) # need to get the salt
+ u && u.authenticated?(password) ? u : nil
+ end
+
+ # Encrypts some data with the salt.
+ def self.encrypt(password, salt)
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
+ end
+
+ # Encrypts the password with the user salt
+ def encrypt(password)
+ self.class.encrypt(password, salt)
+ end
+
+ def authenticated?(password)
+ crypted_password == encrypt(password)
+ end
+
+ def remember_token?
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
+ end
+
+ # These create and unset the fields required for remembering users between browser closes
+ def remember_me
+ self.remember_token_expires_at = 2.weeks.from_now.utc
+ self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
+ save(false)
+ end
+
+ def forget_me
+ self.remember_token_expires_at = nil
+ self.remember_token = nil
+ save(false)
+ end
+
+ protected
+ # before filter
+ def encrypt_password
+ return if password.blank?
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
+ self.crypted_password = encrypt(password)
+ end
+
+ def password_required?
+ crypted_password.blank? || !password.blank?
+ end
+end
20 generators/authenticated/templates/model_controller.rb
@@ -0,0 +1,20 @@
+class <%= model_controller_class_name %>Controller < ApplicationController
+ # Be sure to include AuthenticationSystem in Application Controller instead
+ include AuthenticatedSystem
+ # If you want "remember me" functionality, add the this before_filter uncommended to Application Controller
+ before_filter :login_from_cookie
+
+ # render new.rhtml
+ def new
+ end
+
+ def create
+ @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
+ @<%= file_name %>.save!
+ self.current_<%= file_name %> = @<%= file_name %>
+ redirect_back_or_default('/')
+ flash[:notice] = "Thanks for signing up!"
+ rescue ActiveRecord::RecordInvalid
+ render :action => 'new'
+ end
+end
64 generators/authenticated/templates/model_functional_test.rb
@@ -0,0 +1,64 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require '<%= model_controller_file_name %>_controller'
+
+# Re-raise errors caught by the controller.
+class <%= model_controller_class_name %>Controller; def rescue_action(e) raise e end; end
+
+class <%= model_controller_class_name %>ControllerTest < Test::Unit::TestCase
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
+ # Then, you can remove it from this and the units test.
+ include AuthenticatedTestHelper
+
+ fixtures :<%= table_name %>
+
+ def setup
+ @controller = <%= model_controller_class_name %>Controller.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_should_allow_signup
+ assert_difference <%= class_name %>, :count do
+ create_<%= file_name %>
+ assert_response :redirect
+ end
+ end
+
+ def test_should_require_login_on_signup
+ assert_no_difference <%= class_name %>, :count do
+ create_<%= file_name %>(:login => nil)
+ assert assigns(:<%= file_name %>).errors.on(:login)
+ assert_response :success
+ end
+ end
+
+ def test_should_require_password_on_signup
+ assert_no_difference <%= class_name %>, :count do
+ create_<%= file_name %>(:password => nil)
+ assert assigns(:<%= file_name %>).errors.on(:password)
+ assert_response :success
+ end
+ end
+
+ def test_should_require_password_confirmation_on_signup
+ assert_no_difference <%= class_name %>, :count do
+ create_<%= file_name %>(:password_confirmation => nil)
+ assert assigns(:<%= file_name %>).errors.on(:password_confirmation)
+ assert_response :success
+ end
+ end
+
+ def test_should_require_email_on_signup
+ assert_no_difference <%= class_name %>, :count do
+ create_<%= file_name %>(:email => nil)
+ assert assigns(:<%= file_name %>).errors.on(:email)
+ assert_response :success
+ end
+ end
+
+ protected
+ def create_<%= file_name %>(options = {})
+ post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire@example.com',
+ :password => 'quire', :password_confirmation => 'quire' }.merge(options)
+ end
+end
2 generators/authenticated/templates/model_helper.rb
@@ -0,0 +1,2 @@
+module <%= model_controller_class_name %>Helper
+end
16 generators/authenticated/templates/signup.rhtml
@@ -0,0 +1,16 @@
+<%%= error_messages_for :<%= file_name %> %>
+<%% form_for :<%= file_name %>, :url => <%= table_name %>_path do |f| -%>
+<p><label for="login">Login</label><br/>
+<%%= f.text_field :login %></p>
+
+<p><label for="email">Email</label><br/>
+<%%= f.text_field :email %></p>
+
+<p><label for="password">Password</label><br/>
+<%%= f.password_field :password %></p>
+
+<p><label for="password_confirmation">Confirm Password</label><br/>
+<%%= f.password_field :password_confirmation %></p>
+
+<p><%%= submit_tag 'Sign up' %></p>
+<%% end -%>
74 generators/authenticated/templates/unit_test.rb
@@ -0,0 +1,74 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class <%= class_name %>Test < Test::Unit::TestCase
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead.
+ # Then, you can remove it from this and the functional test.
+ include AuthenticatedTestHelper
+ fixtures :<%= table_name %>
+
+ def test_should_create_<%= file_name %>
+ assert_difference <%= class_name %>, :count do
+ assert create_<%= file_name %>.valid?
+ end
+ end
+
+ def test_should_require_login
+ assert_no_difference <%= class_name %>, :count do
+ u = create_<%= file_name %>(:login => nil)
+ assert u.errors.on(:login)
+ end
+ end
+
+ def test_should_require_password
+ assert_no_difference <%= class_name %>, :count do
+ u = create_<%= file_name %>(:password => nil)
+ assert u.errors.on(:password)
+ end
+ end
+
+ def test_should_require_password_confirmation
+ assert_no_difference <%= class_name %>, :count do
+ u = create_<%= file_name %>(:password_confirmation => nil)
+ assert u.errors.on(:password_confirmation)
+ end
+ end
+
+ def test_should_require_email
+ assert_no_difference <%= class_name %>, :count do
+ u = create_<%= file_name %>(:email => nil)
+ assert u.errors.on(:email)
+ end
+ end
+
+ def test_should_reset_password
+ <%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'new password')
+ end
+
+ def test_should_not_rehash_password
+ <%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin2', 'test')
+ end
+
+ def test_should_authenticate_<%= file_name %>
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
+ end
+
+ def test_should_set_remember_token
+ <%= table_name %>(:quentin).remember_me
+ assert_not_nil <%= table_name %>(:quentin).remember_token
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
+ end
+
+ def test_should_unset_remember_token
+ <%= table_name %>(:quentin).remember_me
+ assert_not_nil <%= table_name %>(:quentin).remember_token
+ <%= table_name %>(:quentin).forget_me
+ assert_nil <%= table_name %>(:quentin).remember_token
+ end
+
+ protected
+ def create_<%= file_name %>(options = {})
+ <%= class_name %>.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
+ end
+end
1 install.rb
@@ -0,0 +1 @@
+puts IO.read(File.join(File.dirname(__FILE__), 'README'))
1 lib/restful_authentication.rb
@@ -0,0 +1 @@
+# RestfulAuthentication
4 tasks/restful_authentication_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :restful_authentication do
+# # Task goes here
+# end
8 test/restful_authentication_test.rb
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+class RestfulAuthenticationTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ flunk
+ end
+end

0 comments on commit 2366199

Please sign in to comment.