[38302] branches/gsoc08-mpwa
digx at macports.org
digx at macports.org
Mon Jul 14 21:48:51 PDT 2008
Revision: 38302
http://trac.macosforge.org/projects/macports/changeset/38302
Author: digx at macports.org
Date: 2008-07-14 21:48:51 -0700 (Mon, 14 Jul 2008)
Log Message:
-----------
Plugins\!
Modified Paths:
--------------
branches/gsoc08-mpwa/Rakefile
Added Paths:
-----------
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/.gitignore
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/CHANGELOG
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/MIT-LICENSE
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/README
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/acts-as-taggable-on.gemspec
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/templates/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/templates/migration.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/init.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts-as-taggable-on.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_taggable_on.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_tagger.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag_list.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tagging.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tags_helper.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/autotest/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/autotest/discover.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/rails/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/rails/init.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_tagger_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_list_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/taggable_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagger_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagging_spec.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/schema.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec.opts
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec_helper.rb
branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/uninstall.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/.gitignore
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/CHANGELOG
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/README
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/Rakefile
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/TODO
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/USAGE
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/authenticated_generator.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/lib/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/lib/insert_routes.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/_model_partial.html.erb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/activation.html.erb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_system.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_test_helper.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/controller.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/helper.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/login.html.erb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/mailer.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/migration.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_controller.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/observer.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup.html.erb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup_notification.html.erb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/site_keys.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/access_control_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/authenticated_system_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/sessions_controller_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/users_controller_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/fixtures/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/fixtures/users.yml
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/helpers/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/helpers/users_helper_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/models/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/models/user_spec.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories_helper.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_navigation_steps.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_resource_steps.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_response_steps.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/user_steps.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/accounts.story
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/sessions.story
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/functional_test.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/mailer_test.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/model_functional_test.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/unit_test.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/init.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/install.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_cookie_token.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_password.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/aasm_roles.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/stateful_roles.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification/email_validation.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification.rb
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/AccessControl.txt
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authentication.txt
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authorization.txt
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/RailsPlugins.txt
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityFramework.graffle
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityFramework.png
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityPatterns.txt
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Tradeoffs.txt
branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Trustification.txt
Modified: branches/gsoc08-mpwa/Rakefile
===================================================================
--- branches/gsoc08-mpwa/Rakefile 2008-07-15 04:48:12 UTC (rev 38301)
+++ branches/gsoc08-mpwa/Rakefile 2008-07-15 04:48:51 UTC (rev 38302)
@@ -10,8 +10,7 @@
require 'tasks/rails'
begin
- require 'vlad'
- Vlad.load
+ require 'config/deploy'
rescue LoadError
# do nothing
end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/.gitignore
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/.gitignore (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/.gitignore 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1 @@
+*.log
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/CHANGELOG
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/CHANGELOG (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/CHANGELOG 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,14 @@
+== 2008-06-23
+
+* Can now find related objects of another class (tristanzdunn)
+* Removed extraneous down migration cruft (azabaj)
+
+== 2008-06-09
+
+ * Added support for Single Table Inheritance
+ * Adding gemspec and rails/init.rb for gemified plugin
+
+== 2007-12-12
+
+ * Added ability to use dynamic tag contexts
+ * Fixed missing migration generator
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/MIT-LICENSE
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/MIT-LICENSE (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/MIT-LICENSE 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,20 @@
+Copyright (c) 2007 Michael Bleigh and Intridea Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/README
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/README (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/README 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,170 @@
+ActsAsTaggableOn
+================
+
+This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
+It has evolved substantially since that point, but all credit goes to him for the
+initial tagging functionality that so many people have used.
+
+For instance, in a social network, a user might have tags that are called skills,
+interests, sports, and more. There is no real way to differentiate between tags and
+so an implementation of this type is not possible with acts as taggable on steroids.
+
+Enter Acts as Taggable On. Rather than tying functionality to a specific keyword
+(namely "tags"), acts as taggable on allows you to specify an arbitrary number of
+tag "contexts" that can be used locally or in combination in the same way steroids
+was used.
+
+Installation
+============
+
+Plugin
+------
+
+Acts As Taggable On is available both as a gem and as a traditional plugin. For the
+traditional plugin you can install like so (Rails 2.1 or later):
+
+ script/plugin install git://github.com/mbleigh/acts-as-taggable-on.git
+
+For earlier versions:
+
+ git clone git://github.com/mbleigh/acts-as-taggable-on.git vendor/plugins/acts-as-taggable-on
+
+GemPlugin
+---------
+
+Acts As Taggable On is also available as a gem plugin using Rails 2.1's gem dependencies.
+To install the gem, add this to your config/environment.rb:
+
+ config.gem "mbleigh-acts-as-taggable-on", :source => "http://gems.github.com", :lib => "acts-as-taggable-on"
+
+After that, you can run "rake gems:install" to install the gem if you don't already have it.
+See http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies for
+additional details about gem dependencies in Rails.
+
+** NOTE **
+Some issues have been experienced with "rake gems:install". If that doesn't work to install the gem,
+try just installing it as a normal gem:
+
+ gem install mbleigh-acts-as-taggable-on --source http://gems.github.com
+
+Post Installation (Rails)
+-------------------------
+1. script/generate acts_as_taggable_on_migration
+2. rake db/migrate
+
+Testing
+=======
+
+Acts As Taggable On uses RSpec for its test coverage. If you already have RSpec on your
+application, the specs will run while using:
+
+rake spec:plugins
+
+Example
+=======
+
+class User < ActiveRecord::Base
+ acts_as_taggable_on :tags, :skills, :interests
+end
+
+ at user = User.new(:name => "Bobby")
+ at user.tag_list = "awesome, slick, hefty" # this should be familiar
+ at user.skill_list = "joking, clowning, boxing" # but you can do it for any context!
+ at user.skill_list # => ["joking","clowning","boxing"] as TagList
+ at user.save
+
+ at user.tags # => [<Tag name:"awesome">,<Tag name:"slick">,<Tag name:"hefty">]
+ at user.skills # => [<Tag name:"joking">,<Tag name:"clowning">,<Tag name:"boxing">]
+
+User.find_tagged_with("awesome", :on => :tags) # => [@user]
+User.find_tagged_with("awesome", :on => :skills) # => []
+
+ at frankie = User.create(:name => "Frankie", :skill_list => "joking, flying, eating")
+User.skill_counts # => [<Tag name="joking" count=2>,<Tag name="clowning" count=1>...]
+ at frankie.skill_counts
+
+Relationships
+====================
+
+You can find objects of the same type based on similar tags on certain contexts.
+Also, objects will be returned in descending order based on the total number of
+matched tags.
+
+ at bobby = User.find_by_name("Bobby")
+ at bobby.skill_list # => ["jogging", "diving"]
+
+ at frankie = User.find_by_name("Frankie")
+ at frankie.skill_list # => ["hacking"]
+
+ at tom = User.find_by_name("Tom")
+ at tom.skill_list # => ["hacking", "jogging", "diving"]
+
+ at tom.find_related_skills # => [<User name="Bobby">,<User name="Frankie">]
+ at bobby.find_related_skills # => [<User name="Tom">]
+ at frankie.find_related_skills # => [<User name="Tom">]
+
+
+Dynamic Tag Contexts
+====================
+
+In addition to the generated tag contexts in the definition, it is also possible
+to allow for dynamic tag contexts (this could be user generated tag contexts!)
+
+ at user = User.new(:name => "Bobby")
+ at user.set_tag_list_on(:customs, "same, as, tag, list")
+ at user.tag_list_on(:customs) # => ["same","as","tag","list"]
+ at user.save
+ at user.tags_on(:customs) # => [<Tag name='same'>,...]
+ at user.tag_counts_on(:customs)
+User.find_tagged_with("same", :on => :customs) # => [@user]
+
+Tag Ownership
+=============
+
+Tags can have owners:
+
+class User < ActiveRecord::Base
+ acts_as_tagger
+end
+
+class Photo < ActiveRecord::Base
+ acts_as_taggable_on :locations
+end
+
+ at some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
+ at some_user.owned_taggings
+ at some_user.owned_tags
+ at some_photo.locations_from(@some_user)
+
+Caveats, Uncharted Waters
+=========================
+
+This plugin is still under active development. Tag caching has not
+been thoroughly (or even casually) tested and may not work as expected.
+
+Contributors
+============
+
+* Michael Bleigh - Original Author
+* Brendan Lim - Related Objects
+* Pradeep Elankumaran - Taggers
+* Sinclair Bain - Patch King
+
+Patch Contributors
+------------------
+
+* tristanzdunn - Related objects of other classes
+* azabaj - Fixed migrate down
+* Peter Cooper - named_scope fix
+* slainer68 - STI fix
+* harrylove - migration instructions and fix-ups
+* lawrencepit - cached tag work
+
+Resources
+=========
+
+* Acts As Community - http://www.actsascommunity.com/projects/acts-as-taggable-on
+* GitHub - http://github.com/mbleigh/acts-as-taggable-on
+* Lighthouse - http://mbleigh.lighthouseapp.com/projects/10116-acts-as-taggable-on
+
+Copyright (c) 2007 Michael Bleigh (http://mbleigh.com/) and Intridea Inc. (http://intridea.com/), released under the MIT license
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/acts-as-taggable-on.gemspec
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/acts-as-taggable-on.gemspec (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/acts-as-taggable-on.gemspec 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,42 @@
+Gem::Specification.new do |s|
+ s.name = "acts-as-taggable-on"
+ s.version = "1.0.1"
+ s.date = "2008-06-10"
+ s.summary = "Tagging for ActiveRecord with custom contexts and advanced features."
+ s.email = "michael at intridea.com"
+ s.homepage = "http://www.actsascommunity.com/projects/acts-as-taggable-on"
+ s.description = "Acts As Taggable On provides the ability to have multiple tag contexts on a single model in ActiveRecord. It also has support for tag clouds, related items, taggers, and more."
+ s.has_rdoc = false
+ s.authors = ["Michael Bleigh"]
+ s.files = [ "CHANGELOG",
+ "MIT-LICENSE",
+ "README",
+ "generators/acts_as_taggable_on_migration",
+ "generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb",
+ "generators/acts_as_taggable_on_migration/templates",
+ "generators/acts_as_taggable_on_migration/templates/add_users_migration.rb",
+ "generators/acts_as_taggable_on_migration/templates/migration.rb",
+ "init.rb",
+ "lib/acts-as-taggable-on.rb",
+ "lib/acts_as_taggable_on/acts_as_taggable_on.rb",
+ "lib/acts_as_taggable_on/acts_as_tagger.rb",
+ "lib/acts_as_taggable_on/tag.rb",
+ "lib/acts_as_taggable_on/tag_list.rb",
+ "lib/acts_as_taggable_on/tagging.rb",
+ "lib/acts_as_taggable_on/tags_helper.rb",
+ "rails/init.rb",
+ "spec/acts_as_taggable_on",
+ "spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb",
+ "spec/acts_as_taggable_on/tag_list_spec.rb",
+ "spec/acts_as_taggable_on/tag_spec.rb",
+ "spec/acts_as_taggable_on/taggable_spec.rb",
+ "spec/acts_as_taggable_on/tagger_spec.rb",
+ "spec/acts_as_taggable_on/tagging_spec.rb",
+ "spec/debug.log",
+ "spec/schema.rb",
+ "spec/spec_helper.rb",
+ "uninstall.rb" ]
+ #s.rdoc_options = ["--main", "README.txt"]
+ #s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
+ #s.add_dependency("mbleigh-mash", [">= 0.0.5"])
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,7 @@
+class ActsAsTaggableOnMigrationGenerator < Rails::Generator::Base
+ def manifest
+ record do |m|
+ m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => "acts_as_taggable_on_migration"
+ end
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/templates/migration.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/templates/migration.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/generators/acts_as_taggable_on_migration/templates/migration.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,29 @@
+class ActsAsTaggableOnMigration < ActiveRecord::Migration
+ def self.up
+ create_table :tags do |t|
+ t.column :name, :string
+ end
+
+ create_table :taggings do |t|
+ t.column :tag_id, :integer
+ t.column :taggable_id, :integer
+ t.column :tagger_id, :integer
+ t.column :tagger_type, :string
+
+ # You should make sure that the column created is
+ # long enough to store the required class names.
+ t.column :taggable_type, :string
+ t.column :context, :string
+
+ t.column :created_at, :datetime
+ end
+
+ add_index :taggings, :tag_id
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
+ end
+
+ def self.down
+ drop_table :taggings
+ drop_table :tags
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/init.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/init.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/init.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1 @@
+require File.dirname(__FILE__) + "/rails/init"
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts-as-taggable-on.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts-as-taggable-on.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts-as-taggable-on.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,6 @@
+require 'acts_as_taggable_on/acts_as_taggable_on'
+require 'acts_as_taggable_on/acts_as_tagger'
+require 'acts_as_taggable_on/tag'
+require 'acts_as_taggable_on/tag_list'
+require 'acts_as_taggable_on/tags_helper'
+require 'acts_as_taggable_on/tagging'
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_taggable_on.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_taggable_on.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_taggable_on.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,310 @@
+module ActiveRecord
+ module Acts
+ module TaggableOn
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def taggable?
+ false
+ end
+
+ def acts_as_taggable
+ acts_as_taggable_on :tags
+ end
+
+ def acts_as_taggable_on(*args)
+ args.flatten! if args
+ args.compact! if args
+ for tag_type in args
+ tag_type = tag_type.to_s
+ self.class_eval do
+ has_many "#{tag_type.singularize}_taggings".to_sym, :as => :taggable, :dependent => :destroy,
+ :include => :tag, :conditions => ["context = ?",tag_type], :class_name => "Tagging"
+ has_many "#{tag_type}".to_sym, :through => "#{tag_type.singularize}_taggings".to_sym, :source => :tag
+ end
+
+ self.class_eval <<-RUBY
+ def self.taggable?
+ true
+ end
+
+ def self.caching_#{tag_type.singularize}_list?
+ caching_tag_list_on?("#{tag_type}")
+ end
+
+ def self.#{tag_type.singularize}_counts(options={})
+ tag_counts_on('#{tag_type}',options)
+ end
+
+ def #{tag_type.singularize}_list
+ tag_list_on('#{tag_type}')
+ end
+
+ def #{tag_type.singularize}_list=(new_tags)
+ set_tag_list_on('#{tag_type}',new_tags)
+ end
+
+ def #{tag_type.singularize}_counts(options = {})
+ tag_counts_on('#{tag_type}',options)
+ end
+
+ def #{tag_type}_from(owner)
+ tag_list_on('#{tag_type}', owner)
+ end
+
+ def find_related_#{tag_type}(options = {})
+ related_tags_for('#{tag_type}', self.class, options)
+ end
+ alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
+
+ def find_related_#{tag_type}_for(klass, options = {})
+ related_tags_for('#{tag_type}', klass, options)
+ end
+ RUBY
+ end
+
+ if respond_to?(:tag_types)
+ write_inheritable_attribute( :tag_types, (tag_types + args).uniq )
+ else
+ self.class_eval do
+ write_inheritable_attribute(:tag_types, args.uniq)
+ class_inheritable_reader :tag_types
+
+ has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
+ has_many :base_tags, :class_name => "Tag", :through => :taggings, :source => :tag
+
+ attr_writer :custom_contexts
+
+ before_save :save_cached_tag_list
+ after_save :save_tags
+ end
+
+ include ActiveRecord::Acts::TaggableOn::InstanceMethods
+ extend ActiveRecord::Acts::TaggableOn::SingletonMethods
+ alias_method_chain :reload, :tag_list
+ end
+ end
+
+ def is_taggable?
+ false
+ end
+ end
+
+ module SingletonMethods
+ # Pass either a tag string, or an array of strings or tags
+ #
+ # Options:
+ # :exclude - Find models that are not tagged with the given tags
+ # :match_all - Find models that match all of the given tags, not just one
+ # :conditions - A piece of SQL conditions to add to the query
+ # :on - scopes the find to a context
+ def find_tagged_with(*args)
+ options = find_options_for_find_tagged_with(*args)
+ options.blank? ? [] : find(:all,options)
+ end
+
+ def caching_tag_list_on?(context)
+ column_names.include?("cached_#{context.to_s.singularize}_list")
+ end
+
+ def tag_counts_on(context, options = {})
+ Tag.find(:all, find_options_for_tag_counts(options.merge({:on => context.to_s})))
+ end
+
+ def find_options_for_find_tagged_with(tags, options = {})
+ tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
+
+ return {} if tags.empty?
+
+ conditions = []
+ conditions << sanitize_sql(options.delete(:conditions)) if options[:conditions]
+
+ unless (on = options.delete(:on)).nil?
+ conditions << sanitize_sql(["context = ?",on.to_s])
+ end
+
+ taggings_alias, tags_alias = "#{table_name}_taggings", "#{table_name}_tags"
+
+ if options.delete(:exclude)
+ tags_conditions = tags.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
+ conditions << sanitize_sql(["#{table_name}.id NOT IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} LEFT OUTER JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id WHERE (#{tags_conditions}) AND #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})", tags])
+ else
+ conditions << tags.map { |t| sanitize_sql(["#{tags_alias}.name LIKE ?", t]) }.join(" OR ")
+
+ if options.delete(:match_all)
+ group = "#{taggings_alias}.taggable_id HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
+ end
+ end
+
+ { :select => "DISTINCT #{table_name}.*",
+ :joins => "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias} ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key} AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)} " +
+ "LEFT OUTER JOIN #{Tag.table_name} #{tags_alias} ON #{tags_alias}.id = #{taggings_alias}.tag_id",
+ :conditions => conditions.join(" AND "),
+ :group => group
+ }.update(options)
+ end
+
+ # Calculate the tag counts for all tags.
+ #
+ # Options:
+ # :start_at - Restrict the tags to those created after a certain time
+ # :end_at - Restrict the tags to those created before a certain time
+ # :conditions - A piece of SQL conditions to add to the query
+ # :limit - The maximum number of tags to return
+ # :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
+ # :at_least - Exclude tags with a frequency less than the given value
+ # :at_most - Exclude tags with a frequency greater than the given value
+ # :on - Scope the find to only include a certain context
+ def find_options_for_tag_counts(options = {})
+ options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on
+
+ scope = scope(:find)
+ start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
+ end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
+
+ type_and_context = "#{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)}"
+
+ conditions = [
+ type_and_context,
+ options[:conditions],
+ start_at,
+ end_at
+ ]
+
+ conditions = conditions.compact.join(' AND ')
+ conditions = merge_conditions(conditions, scope[:conditions]) if scope
+
+ joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
+ joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
+ joins << "LEFT OUTER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
+ joins << scope[:joins] if scope && scope[:joins]
+
+ at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
+ at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
+ having = [at_least, at_most].compact.join(' AND ')
+ group_by = "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING COUNT(*) > 0"
+ group_by << " AND #{having}" unless having.blank?
+
+ { :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
+ :joins => joins.join(" "),
+ :conditions => conditions,
+ :group => group_by
+ }.update(options)
+ end
+
+ def is_taggable?
+ true
+ end
+ end
+
+ module InstanceMethods
+
+ def tag_types
+ self.class.tag_types
+ end
+
+ def custom_contexts
+ @custom_contexts ||= []
+ end
+
+ def is_taggable?
+ self.class.is_taggable?
+ end
+
+ def add_custom_context(value)
+ custom_contexts << value.to_s unless custom_contexts.include?(value.to_s) or self.class.tag_types.map(&:to_s).include?(value.to_s)
+ end
+
+ def tag_list_on(context, owner=nil)
+ var_name = context.to_s.singularize + "_list"
+ add_custom_context(context)
+ return instance_variable_get("@#{var_name}") unless instance_variable_get("@#{var_name}").nil?
+
+ if !owner && self.class.caching_tag_list_on?(context) and !(cached_value = cached_tag_list_on(context)).nil?
+ instance_variable_set("@#{var_name}", TagList.from(self["cached_#{var_name}"]))
+ else
+ instance_variable_set("@#{var_name}", TagList.new(*tags_on(context, owner).map(&:name)))
+ end
+ end
+
+ def tags_on(context, owner=nil)
+ if owner
+ opts = {:conditions => ["context = ? AND tagger_id = ? AND tagger_type = ?",
+ context.to_s, owner.id, owner.class.to_s]}
+ else
+ opts = {:conditions => ["context = ?", context.to_s]}
+ end
+ base_tags.find(:all, opts)
+ end
+
+ def cached_tag_list_on(context)
+ self["cached_#{context.to_s.singularize}_list"]
+ end
+
+ def set_tag_list_on(context,new_list, tagger=nil)
+ instance_variable_set("@#{context.to_s.singularize}_list", TagList.from_owner(tagger, new_list))
+ add_custom_context(context)
+ end
+
+ def tag_counts_on(context,options={})
+ self.class.tag_counts_on(context,{:conditions => ["#{Tag.table_name}.name IN (?)", tag_list_on(context)]}.reverse_merge!(options))
+ end
+
+ def related_tags_for(context, klass, options = {})
+ search_conditions = related_search_options(context, klass, options)
+
+ klass.find(:all, search_conditions)
+ end
+
+ def related_search_options(context, klass, options = {})
+ tags_to_find = self.tags_on(context).collect { |t| t.name }
+
+ { :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
+ :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
+ :conditions => ["#{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?)", tags_to_find],
+ :group => "#{klass.table_name}.id",
+ :order => "count DESC"
+ }.update(options)
+ end
+
+ def save_cached_tag_list
+ self.class.tag_types.map(&:to_s).each do |tag_type|
+ if self.class.send("caching_#{tag_type.singularize}_list?")
+ self["cached_#{tag_type.singularize}_list"] = send("#{tag_type.singularize}_list").to_s
+ end
+ end
+ end
+
+ def save_tags
+ (custom_contexts + self.class.tag_types.map(&:to_s)).each do |tag_type|
+ next unless instance_variable_get("@#{tag_type.singularize}_list")
+ owner = instance_variable_get("@#{tag_type.singularize}_list").owner
+ new_tag_names = instance_variable_get("@#{tag_type.singularize}_list") - tags_on(tag_type).map(&:name)
+ old_tags = tags_on(tag_type).reject { |tag| instance_variable_get("@#{tag_type.singularize}_list").include?(tag.name) }
+
+ self.class.transaction do
+ base_tags.delete(*old_tags) if old_tags.any?
+ new_tag_names.each do |new_tag_name|
+ new_tag = Tag.find_or_create_with_like_by_name(new_tag_name)
+ Tagging.create(:tag_id => new_tag.id, :context => tag_type,
+ :taggable => self, :tagger => owner)
+ end
+ end
+ end
+
+ true
+ end
+
+ def reload_with_tag_list(*args)
+ self.class.tag_types.each do |tag_type|
+ self.instance_variable_set("@#{tag_type.to_s.singularize}_list", nil)
+ end
+
+ reload_without_tag_list(*args)
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_tagger.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_tagger.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_tagger.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,52 @@
+module ActiveRecord
+ module Acts
+ module Tagger
+ def self.included(base)
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_tagger(opts={})
+ has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
+ :include => :tag, :class_name => "Tagging")
+ has_many :owned_tags, :through => :owned_taggings, :source => :tag
+ include ActiveRecord::Acts::Tagger::InstanceMethods
+ extend ActiveRecord::Acts::Tagger::SingletonMethods
+ end
+
+ def is_tagger?
+ false
+ end
+ end
+
+ module InstanceMethods
+ def self.included(base)
+ end
+
+ def tag(taggable, opts={})
+ opts.reverse_merge!(:force => true)
+
+ return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
+ raise "You need to specify a tag context using :on" unless opts.has_key?(:on)
+ raise "You need to specify some tags using :with" unless opts.has_key?(:with)
+ raise "No context :#{opts[:on]} defined in #{taggable.class.to_s}" unless
+ ( opts[:force] || taggable.tag_types.include?(opts[:on]) )
+
+ taggable.set_tag_list_on(opts[:on].to_s, opts[:with], self)
+ taggable.save
+ end
+
+ def is_tagger?
+ self.class.is_tagger?
+ end
+ end
+
+ module SingletonMethods
+ def is_tagger?
+ true
+ end
+ end
+
+ end
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,23 @@
+class Tag < ActiveRecord::Base
+ has_many :taggings
+
+ validates_presence_of :name
+ validates_uniqueness_of :name
+
+ # LIKE is used for cross-database case-insensitivity
+ def self.find_or_create_with_like_by_name(name)
+ find(:first, :conditions => ["name LIKE ?", name]) || create(:name => name)
+ end
+
+ def ==(object)
+ super || (object.is_a?(Tag) && name == object.name)
+ end
+
+ def to_s
+ name
+ end
+
+ def count
+ read_attribute(:count).to_i
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag_list.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag_list.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tag_list.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,93 @@
+class TagList < Array
+ cattr_accessor :delimiter
+ self.delimiter = ','
+
+ def initialize(*args)
+ add(*args)
+ end
+
+ attr_accessor :owner
+
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
+ #
+ # tag_list.add("Fun", "Happy")
+ #
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
+ #
+ # tag_list.add("Fun, Happy", :parse => true)
+ def add(*names)
+ extract_and_apply_options!(names)
+ concat(names)
+ clean!
+ self
+ end
+
+ # Remove specific tags from the tag_list.
+ #
+ # tag_list.remove("Sad", "Lonely")
+ #
+ # Like #add, the <tt>:parse</tt> option can be used to remove multiple tags in a string.
+ #
+ # tag_list.remove("Sad, Lonely", :parse => true)
+ def remove(*names)
+ extract_and_apply_options!(names)
+ delete_if { |name| names.include?(name) }
+ self
+ end
+
+ # Transform the tag_list into a tag string suitable for edting in a form.
+ # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
+ #
+ # tag_list = TagList.new("Round", "Square,Cube")
+ # tag_list.to_s # 'Round, "Square,Cube"'
+ def to_s
+ clean!
+
+ map do |name|
+ name.include?(delimiter) ? "\"#{name}\"" : name
+ end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
+ end
+
+ private
+ # Remove whitespace, duplicates, and blanks.
+ def clean!
+ reject!(&:blank?)
+ map!(&:strip)
+ uniq!
+ end
+
+ def extract_and_apply_options!(args)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ options.assert_valid_keys :parse
+
+ if options[:parse]
+ args.map! { |a| self.class.from(a) }
+ end
+
+ args.flatten!
+ end
+
+ class << self
+ # Returns a new TagList using the given tag string.
+ #
+ # tag_list = TagList.from("One , Two, Three")
+ # tag_list # ["One", "Two", "Three"]
+ def from(string)
+ returning new do |tag_list|
+ string = string.to_s.dup
+
+ # Parse the quoted tags
+ string.gsub!(/"(.*?)"\s*#{delimiter}?\s*/) { tag_list << $1; "" }
+ string.gsub!(/'(.*?)'\s*#{delimiter}?\s*/) { tag_list << $1; "" }
+
+ tag_list.add(string.split(delimiter))
+ end
+ end
+
+ def from_owner(owner, *tags)
+ returning from(*tags) do |taglist|
+ taglist.owner = owner
+ end
+ end
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tagging.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tagging.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tagging.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,6 @@
+class Tagging < ActiveRecord::Base #:nodoc:
+ belongs_to :tag
+ belongs_to :taggable, :polymorphic => true
+ belongs_to :tagger, :polymorphic => true
+ validates_presence_of :context
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tags_helper.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tags_helper.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/tags_helper.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,11 @@
+module TagsHelper
+ # See the README for an example using tag_cloud.
+ def tag_cloud(tags, classes)
+ max_count = tags.sort_by(&:count).last.count.to_f
+
+ tags.each do |tag|
+ index = ((tag.count / max_count) * (classes.size - 1)).round
+ yield tag, classes[index]
+ end
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/autotest/discover.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/autotest/discover.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/lib/autotest/discover.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,6 @@
+# Need this to get picked up by autotest?
+$:.push(File.join(File.dirname(__FILE__), %w[.. .. rspec]))
+
+Autotest.add_discovery do
+ "rspec"
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/rails/init.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/rails/init.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/rails/init.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,6 @@
+require 'acts-as-taggable-on'
+
+ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
+ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
+
+RAILS_DEFAULT_LOGGER.info "** acts_as_taggable_on: initialized properly."
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,151 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe "Acts As Taggable On" do
+ it "should provide a class method 'taggable?' that is false for untaggable models" do
+ UntaggableModel.should_not be_taggable
+ end
+
+ describe "Taggable Method Generation" do
+ before(:each) do
+ [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
+ @taggable = TaggableModel.new(:name => "Bob Jones")
+ end
+
+ it "should respond 'true' to taggable?" do
+ @taggable.class.should be_taggable
+ end
+
+ it "should create a class attribute for tag types" do
+ @taggable.class.should respond_to(:tag_types)
+ end
+
+ it "should generate an association for each tag type" do
+ @taggable.should respond_to(:tags, :skills, :languages)
+ end
+
+ it "should generate a cached column checker for each tag type" do
+ TaggableModel.should respond_to(:caching_tag_list?, :caching_skill_list?, :caching_language_list?)
+ end
+
+ it "should add tagged_with and tag_counts to singleton" do
+ TaggableModel.should respond_to(:find_tagged_with, :tag_counts)
+ end
+
+ it "should add saving of tag lists and cached tag lists to the instance" do
+ @taggable.should respond_to(:save_cached_tag_list)
+ @taggable.should respond_to(:save_tags)
+ end
+
+ it "should generate a tag_list accessor/setter for each tag type" do
+ @taggable.should respond_to(:tag_list, :skill_list, :language_list)
+ @taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
+ end
+ end
+
+ describe "Single Table Inheritance" do
+ before do
+ @taggable = TaggableModel.new(:name => "taggable")
+ @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
+ @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
+ end
+
+ it "should pass on tag contexts to STI-inherited models" do
+ @inherited_same.should respond_to(:tag_list, :skill_list, :language_list)
+ @inherited_different.should respond_to(:tag_list, :skill_list, :language_list)
+ end
+
+ it "should have tag contexts added in altered STI models" do
+ @inherited_different.should respond_to(:part_list)
+ end
+ end
+
+ describe "Reloading" do
+ it "should save a model instantiated by Model.find" do
+ taggable = TaggableModel.create!(:name => "Taggable")
+ found_taggable = TaggableModel.find(taggable.id)
+ found_taggable.save
+ end
+ end
+
+ describe "Related Objects" do
+ it "should find related objects based on tag names on context" do
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
+ taggable3 = TaggableModel.create!(:name => "Taggable 3")
+
+ taggable1.tag_list = "one, two"
+ taggable1.save
+
+ taggable2.tag_list = "three, four"
+ taggable2.save
+
+ taggable3.tag_list = "one, four"
+ taggable3.save
+
+ taggable1.find_related_tags.should include(taggable3)
+ taggable1.find_related_tags.should_not include(taggable2)
+ end
+
+ it "should find other related objects based on tag names on context" do
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
+ taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
+ taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
+
+ taggable1.tag_list = "one, two"
+ taggable1.save
+
+ taggable2.tag_list = "three, four"
+ taggable2.save
+
+ taggable3.tag_list = "one, four"
+ taggable3.save
+
+ taggable1.find_related_tags_for(OtherTaggableModel).should include(taggable3)
+ taggable1.find_related_tags_for(OtherTaggableModel).should_not include(taggable2)
+ end
+ end
+
+ describe 'Tagging Contexts' do
+ before(:all) do
+ class Array
+ def freq
+ k=Hash.new(0)
+ self.each {|e| k[e]+=1}
+ k
+ end
+ end
+ end
+
+ it 'should eliminate duplicate tagging contexts ' do
+ TaggableModel.acts_as_taggable_on(:skills, :skills)
+ TaggableModel.tag_types.freq[:skills].should_not == 3
+ end
+
+ it "should not contain embedded/nested arrays" do
+ TaggableModel.acts_as_taggable_on([:array], [:array])
+ TaggableModel.tag_types.freq[[:array]].should == 0
+ end
+
+ it "should _flatten_ the content of arrays" do
+ TaggableModel.acts_as_taggable_on([:array], [:array])
+ TaggableModel.tag_types.freq[:array].should == 1
+ end
+
+ it "should not raise an error when passed nil" do
+ lambda {
+ TaggableModel.acts_as_taggable_on()
+ }.should_not raise_error
+ end
+
+ it "should not raise an error when passed [nil]" do
+ lambda {
+ TaggableModel.acts_as_taggable_on([nil])
+ }.should_not raise_error
+ end
+
+ after(:all) do
+ class Array; remove_method :freq; end
+ end
+ end
+
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_tagger_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_tagger_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/acts_as_tagger_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,72 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe "acts_as_tagger" do
+ context "Tagger Method Generation" do
+
+ before(:each) do
+ @tagger = TaggableUser.new()
+ end
+
+ it "should add #is_tagger? query method to the class-side" do
+ TaggableUser.should respond_to(:is_tagger?)
+ end
+
+ it "should return true from the class-side #is_tagger?" do
+ TaggableUser.is_tagger?.should be_true
+ end
+
+ it "should return false from the base #is_tagger?" do
+ ActiveRecord::Base.is_tagger?.should be_false
+ end
+
+ it "should add #is_tagger? query method to the singleton" do
+ @tagger.should respond_to(:is_tagger?)
+ end
+
+ it "should add #tag method on the instance-side" do
+ @tagger.should respond_to(:tag)
+ end
+
+ it "should generate an association for #owned_taggings and #owned_tags" do
+ @tagger.should respond_to(:owned_taggings, :owned_tags)
+ end
+ end
+
+ describe "#tag" do
+ context 'when called with a non-existent tag context' do
+ before(:each) do
+ @tagger = TaggableUser.new()
+ @taggable = TaggableModel.new(:name=>"Richard Prior")
+ end
+
+ it "should by default not throw an exception " do
+ @taggable.tag_list_on(:foo).should be_empty
+ lambda {
+ @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo)
+ }.should_not raise_error
+ end
+
+ it 'should by default create the tag context on-the-fly' do
+ @taggable.tag_list_on(:here_ond_now).should be_empty
+ @tagger.tag(@taggable, :with=>'that', :on=>:here_ond_now)
+ @taggable.tag_list_on(:here_ond_now).should include('that')
+ end
+
+ it "should throw an exception when the default is over-ridden" do
+ @taggable.tag_list_on(:foo_boo).should be_empty
+ lambda {
+ @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false)
+ }.should raise_error
+ end
+
+ it "should not create the tag context on-the-fly when the default is over-ridden" do
+ @taggable.tag_list_on(:foo_boo).should be_empty
+ @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false) rescue
+ @taggable.tag_list_on(:foo_boo).should be_empty
+ end
+
+ end
+
+ end
+
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_list_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_list_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_list_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,41 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe TagList do
+ before(:each) do
+ @tag_list = TagList.new("awesome","radical")
+ end
+
+ it "should be an array" do
+ @tag_list.is_a?(Array).should be_true
+ end
+
+ it "should be able to be add a new tag word" do
+ @tag_list.add("cool")
+ @tag_list.include?("cool").should be_true
+ end
+
+ it "should be able to add delimited lists of words" do
+ @tag_list.add("cool, wicked", :parse => true)
+ @tag_list.include?("cool").should be_true
+ @tag_list.include?("wicked").should be_true
+ end
+
+ it "should be able to remove words" do
+ @tag_list.remove("awesome")
+ @tag_list.include?("awesome").should be_false
+ end
+
+ it "should be able to remove delimited lists of words" do
+ @tag_list.remove("awesome, radical", :parse => true)
+ @tag_list.should be_empty
+ end
+
+ it "should give a delimited list of words when converted to string" do
+ @tag_list.to_s.should == "awesome, radical"
+ end
+
+ it "should quote escape tags with commas in them" do
+ @tag_list.add("cool","rad,bodacious")
+ @tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tag_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,25 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Tag do
+ before(:each) do
+ @tag = Tag.new
+ @user = TaggableModel.create(:name => "Pablo")
+ end
+
+ it "should require a name" do
+ @tag.should have(1).errors_on(:name)
+ @tag.name = "something"
+ @tag.should have(0).errors_on(:name)
+ end
+
+ it "should equal a tag with the same name" do
+ @tag.name = "awesome"
+ new_tag = Tag.new(:name => "awesome")
+ new_tag.should == @tag
+ end
+
+ it "should return its name when to_s is called" do
+ @tag.name = "cool"
+ @tag.to_s.should == "cool"
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/taggable_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/taggable_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/taggable_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,127 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe "Taggable" do
+ before(:each) do
+ [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
+ @taggable = TaggableModel.new(:name => "Bob Jones")
+ end
+
+ it "should be able to create tags" do
+ @taggable.skill_list = "ruby, rails, css"
+ @taggable.instance_variable_get("@skill_list").instance_of?(TagList).should be_true
+ @taggable.save
+
+ Tag.find(:all).size.should == 3
+ end
+
+ it "should be able to create tags through the tag list directly" do
+ @taggable.tag_list_on(:test).add("hello")
+ @taggable.save
+ @taggable.reload
+ @taggable.tag_list_on(:test).should == ["hello"]
+ end
+
+ it "should differentiate between contexts" do
+ @taggable.skill_list = "ruby, rails, css"
+ @taggable.tag_list = "ruby, bob, charlie"
+ @taggable.save
+ @taggable.reload
+ @taggable.skill_list.include?("ruby").should be_true
+ @taggable.skill_list.include?("bob").should be_false
+ end
+
+ it "should be able to remove tags through list alone" do
+ @taggable.skill_list = "ruby, rails, css"
+ @taggable.save
+ @taggable.reload
+ @taggable.should have(3).skills
+ @taggable.skill_list = "ruby, rails"
+ @taggable.save
+ @taggable.reload
+ @taggable.should have(2).skills
+ end
+
+ it "should be able to find by tag" do
+ @taggable.skill_list = "ruby, rails, css"
+ @taggable.save
+ TaggableModel.find_tagged_with("ruby").first.should == @taggable
+ end
+
+ it "should be able to find by tag with context" do
+ @taggable.skill_list = "ruby, rails, css"
+ @taggable.tag_list = "bob, charlie"
+ @taggable.save
+ TaggableModel.find_tagged_with("ruby").first.should == @taggable
+ TaggableModel.find_tagged_with("bob", :on => :skills).first.should_not == @taggable
+ TaggableModel.find_tagged_with("bob", :on => :tags).first.should == @taggable
+ end
+
+ it "should not care about case" do
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby")
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "Ruby")
+
+ Tag.find(:all).size.should == 1
+ TaggableModel.find_tagged_with("ruby").should == TaggableModel.find_tagged_with("Ruby")
+ end
+
+ it "should be able to get tag counts on model as a whole" do
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
+ TaggableModel.tag_counts.should_not be_empty
+ TaggableModel.skill_counts.should_not be_empty
+ end
+
+ it "should be able to get tag counts on an association" do
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
+ bob.tag_counts.first.count.should == 2
+ charlie.skill_counts.first.count.should == 1
+ end
+
+ it "should be able to set a custom tag context list" do
+ bob = TaggableModel.create(:name => "Bob")
+ bob.set_tag_list_on(:rotors, "spinning, jumping")
+ bob.tag_list_on(:rotors).should == ["spinning","jumping"]
+ bob.save
+ bob.reload
+ bob.tags_on(:rotors).should_not be_empty
+ end
+
+ it "should be able to find tagged on a custom tag context" do
+ bob = TaggableModel.create(:name => "Bob")
+ bob.set_tag_list_on(:rotors, "spinning, jumping")
+ bob.tag_list_on(:rotors).should == ["spinning","jumping"]
+ bob.save
+ TaggableModel.find_tagged_with("spinning", :on => :rotors).should_not be_empty
+ end
+
+ describe "Single Table Inheritance" do
+ before do
+ [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
+ @taggable = TaggableModel.new(:name => "taggable")
+ @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
+ @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
+ end
+
+ it "should be able to save tags for inherited models" do
+ @inherited_same.tag_list = "bob, kelso"
+ @inherited_same.save
+ InheritingTaggableModel.find_tagged_with("bob").first.should == @inherited_same
+ end
+
+ it "should find STI tagged models on the superclass" do
+ @inherited_same.tag_list = "bob, kelso"
+ @inherited_same.save
+ TaggableModel.find_tagged_with("bob").first.should == @inherited_same
+ end
+
+ it "should be able to add on contexts only to some subclasses" do
+ @inherited_different.part_list = "fork, spoon"
+ @inherited_different.save
+ InheritingTaggableModel.find_tagged_with("fork", :on => :parts).should be_empty
+ AlteredInheritingTaggableModel.find_tagged_with("fork", :on => :parts).first.should == @inherited_different
+ end
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagger_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagger_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagger_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,23 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe "Tagger" do
+ before(:each) do
+ [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
+ @user = TaggableUser.new
+ @taggable = TaggableModel.new(:name => "Bob Jones")
+ end
+
+ it "should have taggings" do
+ @user.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
+ @user.owned_taggings.size == 2
+ end
+
+ it "should have tags" do
+ @user.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
+ @user.owned_tags.size == 2
+ end
+
+ it "is tagger" do
+ @user.is_tagger?.should(be_true)
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagging_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagging_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/acts_as_taggable_on/tagging_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,7 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Tagging do
+ before(:each) do
+ @tagging = Tagging.new
+ end
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/schema.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/schema.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/schema.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,32 @@
+ActiveRecord::Schema.define :version => 0 do
+ create_table "taggings", :force => true do |t|
+ t.integer "tag_id", :limit => 11
+ t.integer "taggable_id", :limit => 11
+ t.string "taggable_type"
+ t.string "context"
+ t.datetime "created_at"
+ t.integer "tagger_id", :limit => 11
+ t.string "tagger_type"
+ end
+
+ add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
+ add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
+
+ create_table "tags", :force => true do |t|
+ t.string "name"
+ end
+
+ create_table :taggable_models, :force => true do |t|
+ t.column :name, :string
+ t.column :type, :string
+ #t.column :cached_tag_list, :string
+ end
+ create_table :taggable_users, :force => true do |t|
+ t.column :name, :string
+ end
+ create_table :other_taggable_models, :force => true do |t|
+ t.column :name, :string
+ t.column :type, :string
+ #t.column :cached_tag_list, :string
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec.opts
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec.opts (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec.opts 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,7 @@
+--colour
+--format
+specdoc
+--loadby
+mtime
+--reverse
+--backtrace
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec_helper.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec_helper.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/spec/spec_helper.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,33 @@
+require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
+
+module Spec::Example::ExampleGroupMethods
+ alias :context :describe
+end
+
+plugin_spec_dir = File.dirname(__FILE__)
+ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
+
+load(File.dirname(__FILE__) + '/schema.rb')
+
+class TaggableModel < ActiveRecord::Base
+ acts_as_taggable_on :tags, :languages
+ acts_as_taggable_on :skills
+end
+
+class OtherTaggableModel < ActiveRecord::Base
+ acts_as_taggable_on :tags, :languages
+end
+
+class InheritingTaggableModel < TaggableModel
+end
+
+class AlteredInheritingTaggableModel < TaggableModel
+ acts_as_taggable_on :parts
+end
+
+class TaggableUser < ActiveRecord::Base
+ acts_as_tagger
+end
+
+class UntaggableModel < ActiveRecord::Base
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/uninstall.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/uninstall.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/acts-as-taggable-on/uninstall.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1 @@
+# Uninstall hook code here
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/.gitignore
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/.gitignore (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/.gitignore 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,20 @@
+Icon?
+.DS_Store
+TAGS
+REVISION
+*.tmproj
+.settings
+.project
+.tasks-cache
+.svn
+/log/*.log
+/tmp/**/*
+/config/database.yml
+actionmailer_config_DONOTVERSION.rb
+*DONOTVERSION*
+/vendor/src/**/*
+/db/*.sqlite*
+/public/ac/*
+/coverage
+/doc/app
+/doc/plugins
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/CHANGELOG
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/CHANGELOG (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/CHANGELOG 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,68 @@
+h1. Internal Changes to code
+
+As always, this is just a copy-and-pasted version of the CHANGELOG file in the source code tree.
+
+h2. Changes for the May, 2008 version of restful-authentication
+
+h3. Changes to user model
+
+* recently_activated? belongs only if stateful
+* Gave migration a 40-char limit on remember_token & an index on users by login
+* **Much** stricter login and email validation
+* put length constraints in migration too
+* password in 6, 40
+* salt and remember_token now much less predictability
+
+h3. Changes to session_controller
+
+* use uniform logout function
+* use uniform remember_cookie functions
+* avoid calling logged_in? which will auto-log-you-in (safe in the face of
+ logout! call, but idiot-proof)
+* Moved reset_session into only the "now logged in" branch
+** wherever it goes, it has to be in front of the current_user= call
+** See more in README-Tradeoffs.txt
+* made a place to take action on failed login attempt
+* recycle login and remember_me setting on failed login
+* nil'ed out the password field in 'new' view
+
+h3. Changes to users_controller
+
+* use uniform logout function
+* use uniform remember_cookie functions
+* Moved reset_session into only the "now logged in" branch
+** wherever it goes, it has to be in front of the current_user= call
+** See more in README-Tradeoffs.txt
+* made the implicit login only happen for non-activationed sites
+* On a failed signup, kick you back to the signin screen (but strip out the password & confirmation)
+* more descriptive error messages in activate()
+
+h3. users_helper
+
+* link_to_user, link_to_current_user, link_to_signin_with_IP
+* if_authorized(action, resource, &block) view function (with appropriate
+ warning)
+
+h3. authenticated_system
+
+* Made authorized? take optional arguments action=nil, resource=nil, *args
+ This makes its signature better match traditional approaches to access control
+ eg Reference Monitor in "Security Patterns":http://www.securitypatterns.org/patterns.html)
+* authorized? should be a helper too
+* added uniform logout! methods
+* format.any (as found in access_denied) doesn't work until
+ http://dev.rubyonrails.org/changeset/8987 lands.
+* cookies are now refreshed each time we cross the logged out/in barrier, as
+ "best":http://palisade.plynt.com/issues/2004Jul/safe-auth-practices/
+ "practice":http://www.owasp.org/index.php/Session_Management#Regeneration_of_Session_Tokens
+
+h3. Other
+
+* Used escapes <%= %> in email templates (among other reasons, so courtenay's
+ "'dumbass' test":http://tinyurl.com/684g9t doesn't complain)
+* Added site key to generator, users.yml.
+* Made site key generation idempotent in the most crude and hackish way
+* 100% coverage apart from the stateful code. (needed some access_control
+ checks, and the http_auth stuff)
+* Stories!
+
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/README
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/README (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/README 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,176 @@
+h1. Restful Authentication Generator
+
+This widely-used plugin provides a foundation for securely managing user
+authentication:
+* Login / logout
+* Secure password handling
+* Account activation by validating email
+* Account approval / disabling by admin
+* Rudimentary hooks for authorization and access control.
+
+Several features were updated in May, 2008. The newest version of this plugin
+may be found in
+ http://github.com/technoweenie/restful-authentication/tree/master
+While a "classic" (backward-compatible) version may be found in
+ http://github.com/technoweenie/restful-authentication/tree/classic
+
+ !! important: if you upgrade your site, existing user account !!
+ !! passwords will stop working unless you use --old-passwords !!
+
+This page has notes on
+* "Installation":#INSTALL
+* "Compatibility Warning":#COMPATIBILITY
+* "New Features":#AWESOME
+* "After installing":#POST-INSTALL
+
+See the "wiki":http://github.com/technoweenie/restful-authentication/wikis/home
+(or the notes/ directory) if you want to learn more about:
+
+* "Security Design Patterns":Security-Patterns with "snazzy diagram":http://github.com/technoweenie/restful-authentication/tree/master/notes/SecurityFramework.png
+* [[Authentication]] -- Lets a visitor identify herself (and lay claim to her corresponding Roles and measure of Trust)
+* "Trust Metrics":Trustification -- Confidence we can rely on the outcomes of this visitor's actions.
+* [[Authorization]] and Policy -- Based on trust and identity, what actions may this visitor perform?
+* [[Access Control]] -- How the Authorization policy is actually enforced in your code (A: hopefully without turning it into a spaghetti of if thens)
+* [[Rails Plugins]] for Authentication, Trust, Authorization and Access Control
+* [[Tradeoffs]] -- for the paranoid or the curious, a rundown of tradeoffs made in the code
+* [[CHANGELOG]] -- Summary of changes to internals
+* [[TODO]] -- Ideas for how you can help
+
+These best version of the release notes are in the notes/ directory in the
+"source code":http://github.com/technoweenie/restful-authentication/tree/master
+-- look there for the latest version. The wiki versions are taken (manually)
+from there.
+
+***************************************************************************
+<a id="AWESOME"/> </a>
+h2. Exciting new features
+
+h3. Stories
+
+There are now RSpec stories that allow expressive, enjoyable tests for the
+authentication code. The flexible code for resource testing in stories was
+extended from "Ben Mabey's.":http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/
+
+h3. Modularize to match security design patterns:
+
+* Authentication (currently: password, browser cookie token, HTTP basic)
+* Trust metric (email validation)
+* Authorization (stateful roles)
+* Leave a flexible framework that will play nicely with other access control / policy definition / trust metric plugins
+
+h3. Other
+
+* Added a few helper methods for linking to user pages
+* Uniform handling of logout, remember_token
+* Stricter email, login field validation
+* Minor security fixes -- see CHANGELOG
+
+***************************************************************************
+<a id="COMPATIBILITY"/> </a>
+h2. Non-backwards compatible Changes
+
+Here are a few changes in the May 2008 release that increase "Defense in Depth"
+but may require changes to existing accounts
+
+* If you have an existing site, none of these changes are compelling enough to
+ warrant migrating your userbase.
+* If you are generating for a new site, all of these changes are low-impact.
+ You should apply them.
+
+h3. Passwords
+
+The new password encryption (using a site key salt and stretching) will break
+existing user accounts' passwords. We recommend you use the --old-passwords
+option or write a migration tool and submit it as a patch. See the
+[[Tradeoffs]] note for more information.
+
+h3. Validations
+
+By default,
+
+***************************************************************************
+<a id="INSTALL"/> </a>
+h2. Installation
+
+This is a basic restful authentication generator for rails, taken from
+acts as authenticated. Currently it requires Rails 1.2.6 or above.
+
+To use:
+
+ ./script/generate authenticated user sessions \
+ --include-activation \
+ --stateful \
+ --rspec \
+ --skip-migration \
+ --skip-routes \
+ --old-passwords
+
+* 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. You probably want to say "User" here.
+
+* The second parameter specifies the session controller name. This is the
+ controller that handles the actual login/logout function on the site.
+ (probably: "Session").
+
+* --include-activation: Generates the code for a ActionMailer and its respective
+ Activation Code through email.
+
+* --stateful: Builds in support for acts_as_state_machine and generates
+ activation code. (@--stateful@ implies @--include-activation@). Based on the
+ idea at [[http://www.vaporbase.com/postings/stateful_authentication]]. Passing
+ @--skip-migration@ will skip the user migration, and @--skip-routes@ will skip
+ resource generation -- both useful if you've already run this generator.
+
+* --aasm: Works the same as stateful but uses the updated aasm gem
+
+* --rspec: Generate RSpec tests and Stories in place of standard rails tests.
+ This requires the
+ "RSpec and Rspec-on-rails plugins":http://rspec.info/
+ (make sure you "./script/generate rspec" after installing RSpec.) The rspec
+ and story suite are much more thorough than the rails tests, and changes are
+ unlikely to be backported.
+
+* --old-passwords: Use the older password scheme (see [[#COMPATIBILITY]], above)
+
+* --skip-migration: Don't generate a migration file for this model
+
+* --skip-routes: Don't generate a resource line in @config/routes.rb@
+
+
+***************************************************************************
+<a id="POST-INSTALL"/> </a>
+h2. After installing
+
+The below assumes a Model named 'User' and a Controller named 'Session'; please
+alter to suit. There are additional security minutae in @notes/README-Tradeoffs@
+-- only the paranoid or the curious need bother, though.
+
+* Add these familiar login URLs to your @config/routes.rb@ if you like:
+
+ map.signup '/signup', :controller => 'users', :action => 'new' @
+ map.login '/login', :controller => 'sessions', :action => 'new' @
+ map.logout '/logout', :controller => 'sessions', :action => 'destroy' @
+
+* With @--include-activation@, also add to your @config/routes.rb@:
+
+ map.activate '/activate/:activation_code', :controller => 'users', :action => 'activate', :activation_code => nil)
+
+ and add an observer to @config/environment.rb@:
+
+ config.active_record.observers = :users_observer
+
+* With @--stateful@, add an observer to config/environment.rb:
+
+ config.active_record.observers = :user_observer
+
+ and modify the users resource line to read
+
+ map.resources :users, :member => { :suspend => :put,
+ :unsuspend => :put,
+ :purge => :delete }
+
+* If you use a public repository for your code (such as github, rubyforge,
+ gitorious, etc.) make sure to NOT post your site_keys.rb (add a line like
+ '/config/initializers/site_keys.rb' to your .gitignore or do the svn ignore
+ dance), but make sure you DO keep it backed up somewhere safe.
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/Rakefile
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/Rakefile (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/Rakefile 2008-07-15 04:48:51 UTC (rev 38302)
@@ -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
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/TODO
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/TODO (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/TODO 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,15 @@
+
+h3. Authentication security projects for a later date
+
+
+* Track 'failed logins this hour' and demand a captcha after say 5 failed logins
+ ("RECAPTCHA plugin.":http://agilewebdevelopment.com/plugins/recaptcha)
+ "De-proxy-ficate IP address": http://wiki.codemongers.com/NginxHttpRealIpModule
+
+* Make cookie spoofing a little harder: we set the user's cookie to
+ (remember_token), but store digest(remember_token, request_IP). A CSRF cookie
+ spoofer has to then at least also spoof the user's originating IP
+ (see "Secure Programs HOWTO":http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/web-authentication.html)
+
+* Log HTTP request on authentication / authorization failures
+ http://palisade.plynt.com/issues/2004Jul/safe-auth-practices
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/USAGE
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/USAGE (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/USAGE 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1 @@
+./script/generate authenticated USERMODEL CONTROLLERNAME
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/authenticated_generator.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/authenticated_generator.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/authenticated_generator.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,478 @@
+require File.expand_path(File.dirname(__FILE__) + "/lib/insert_routes.rb")
+require 'digest/sha1'
+class AuthenticatedGenerator < Rails::Generator::NamedBase
+ default_options :skip_migration => false,
+ :skip_routes => false,
+ :old_passwords => false,
+ :include_activation => false
+
+ 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,
+ :controller_routing_name, # new_session_path
+ :controller_routing_path, # /session/new
+ :controller_controller_name, # sessions
+ :controller_file_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,
+ :model_controller_routing_name, # new_user_path
+ :model_controller_routing_path, # /users/new
+ :model_controller_controller_name # users
+ 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
+
+ @rspec = has_rspec?
+
+ @controller_name = (args.shift || 'sessions').pluralize
+ @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_file_name, @controller_plural_name = inflect_names(base_name)
+ @controller_singular_name = @controller_file_name.singularize
+ 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
+ @controller_routing_name = @controller_singular_name
+ @controller_routing_path = @controller_file_path.singularize
+ @controller_controller_name = @controller_plural_name
+
+ # 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
+ @model_controller_routing_name = @table_name
+ @model_controller_routing_path = @model_controller_file_path
+ @model_controller_controller_name = @model_controller_plural_name
+
+ load_or_initialize_site_keys()
+
+ if options[:dump_generator_attribute_names]
+ dump_generator_attribute_names
+ 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}", "#{class_name}Mailer", "#{class_name}MailerTest", "#{class_name}Observer"
+ 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('app/views', class_path, "#{file_name}_mailer") if options[:include_activation]
+
+ 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('config/initializers')
+
+ if @rspec
+ m.directory File.join('spec/controllers', controller_class_path)
+ m.directory File.join('spec/controllers', model_controller_class_path)
+ m.directory File.join('spec/models', class_path)
+ m.directory File.join('spec/helpers', model_controller_class_path)
+ m.directory File.join('spec/fixtures', class_path)
+ m.directory File.join('stories', model_controller_file_path)
+ m.directory File.join('stories', 'steps')
+ else
+ m.directory File.join('test/functional', controller_class_path)
+ m.directory File.join('test/functional', model_controller_class_path)
+ m.directory File.join('test/unit', class_path)
+ m.directory File.join('test/fixtures', class_path)
+ end
+
+ m.template 'model.rb',
+ File.join('app/models',
+ class_path,
+ "#{file_name}.rb")
+
+ if options[:include_activation]
+ %w( mailer observer ).each do |model_type|
+ m.template "#{model_type}.rb", File.join('app/models',
+ class_path,
+ "#{file_name}_#{model_type}.rb")
+ end
+ end
+
+ 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 'site_keys.rb', site_keys_file
+
+ if @rspec
+ # RSpec Specs
+ m.template 'spec/controllers/users_controller_spec.rb',
+ File.join('spec/controllers',
+ model_controller_class_path,
+ "#{model_controller_file_name}_controller_spec.rb")
+ m.template 'spec/controllers/sessions_controller_spec.rb',
+ File.join('spec/controllers',
+ controller_class_path,
+ "#{controller_file_name}_controller_spec.rb")
+ m.template 'spec/controllers/access_control_spec.rb',
+ File.join('spec/controllers',
+ controller_class_path,
+ "access_control_spec.rb")
+ m.template 'spec/controllers/authenticated_system_spec.rb',
+ File.join('spec/controllers',
+ controller_class_path,
+ "authenticated_system_spec.rb")
+ m.template 'spec/helpers/users_helper_spec.rb',
+ File.join('spec/helpers',
+ model_controller_class_path,
+ "#{table_name}_helper_spec.rb")
+ m.template 'spec/models/user_spec.rb',
+ File.join('spec/models',
+ class_path,
+ "#{file_name}_spec.rb")
+ m.template 'spec/fixtures/users.yml',
+ File.join('spec/fixtures',
+ class_path,
+ "#{table_name}.yml")
+
+ # RSpec Stories
+ m.template 'stories/steps/ra_navigation_steps.rb',
+ File.join('stories/steps/ra_navigation_steps.rb')
+ m.template 'stories/steps/ra_response_steps.rb',
+ File.join('stories/steps/ra_response_steps.rb')
+ m.template 'stories/steps/ra_resource_steps.rb',
+ File.join('stories/steps/ra_resource_steps.rb')
+ m.template 'stories/steps/user_steps.rb',
+ File.join('stories/steps/', "#{file_name}_steps.rb")
+ m.template 'stories/users/accounts.story',
+ File.join('stories', model_controller_file_path, 'accounts.story')
+ m.template 'stories/users/sessions.story',
+ File.join('stories', model_controller_file_path, 'sessions.story')
+ m.template 'stories/rest_auth_stories_helper.rb',
+ File.join('stories', 'rest_auth_stories_helper.rb')
+ m.template 'stories/rest_auth_stories.rb',
+ File.join('stories', 'rest_auth_stories.rb')
+
+ else
+ m.template 'test/functional_test.rb',
+ File.join('test/functional',
+ controller_class_path,
+ "#{controller_file_name}_controller_test.rb")
+ m.template 'test/model_functional_test.rb',
+ File.join('test/functional',
+ model_controller_class_path,
+ "#{model_controller_file_name}_controller_test.rb")
+ m.template 'test/unit_test.rb',
+ File.join('test/unit',
+ class_path,
+ "#{file_name}_test.rb")
+ if options[:include_activation]
+ m.template 'test/mailer_test.rb', File.join('test/unit', class_path, "#{file_name}_mailer_test.rb")
+ end
+ m.template 'spec/fixtures/users.yml',
+ File.join('test/fixtures',
+ class_path,
+ "#{table_name}.yml")
+ end
+
+ 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")
+
+
+ # Controller templates
+ m.template 'login.html.erb', File.join('app/views', controller_class_path, controller_file_name, "new.html.erb")
+ m.template 'signup.html.erb', File.join('app/views', model_controller_class_path, model_controller_file_name, "new.html.erb")
+ m.template '_model_partial.html.erb', File.join('app/views', model_controller_class_path, model_controller_file_name, "_#{file_name}_bar.html.erb")
+
+ if options[:include_activation]
+ # Mailer templates
+ %w( activation signup_notification ).each do |action|
+ m.template "#{action}.html.erb",
+ File.join('app/views', "#{file_name}_mailer", "#{action}.html.erb")
+ end
+ end
+
+ 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
+ unless options[:skip_routes]
+ # Note that this fails for nested classes -- you're on your own with setting up the routes.
+ m.route_resource controller_singular_name
+ m.route_resources model_controller_plural_name
+ m.route_name('signup', '/signup', {:controller => model_controller_plural_name, :action => 'new'})
+ m.route_name('register', '/register', {:controller => model_controller_plural_name, :action => 'create'})
+ m.route_name('login', '/login', {:controller => controller_controller_name, :action => 'new'})
+ m.route_name('logout', '/logout', {:controller => controller_controller_name, :action => 'destroy'})
+ end
+ end
+
+ #
+ # Post-install notes
+ #
+ action = File.basename($0) # grok the action from './script/generate' or whatever
+ case action
+ when "generate"
+ puts "Ready to generate."
+ puts ("-" * 70)
+ puts "Once finished, don't forget to:"
+ puts
+ if options[:include_activation]
+ puts "- Add an observer to config/environment.rb"
+ puts " config.active_record.observers = :#{file_name}_observer"
+ end
+ if options[:aasm]
+ puts "- Install the acts_as_state_machine gem:"
+ puts " sudo gem sources -a http://gems.github.com (If you haven't already)"
+ puts " sudo gem install rubyist-aasm"
+ elsif options[:stateful]
+ puts "- Install the acts_as_state_machine plugin:"
+ puts " svn export http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk vendor/plugins/acts_as_state_machine"
+ end
+ puts "- Add routes to these resources. In config/routes.rb, insert routes like:"
+ 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')
+ if options[:include_activation]
+ puts %( map.activate '/activate/:activation_code', :controller => '#{model_controller_file_name}', :action => 'activate', :activation_code => nil)
+ end
+ if options[:stateful]
+ puts " and modify the map.resources :#{model_controller_file_name} line to include these actions:"
+ puts " map.resources :#{model_controller_file_name}, :member => { :suspend => :put, :unsuspend => :put, :purge => :delete }"
+ end
+ puts
+ puts ("-" * 70)
+ puts
+ if $rest_auth_site_key_from_generator.blank?
+ puts "You've set a nil site key. This preserves existing users' passwords,"
+ puts "but allows dictionary attacks in the unlikely event your database is"
+ puts "compromised and your site code is not. See the README for more."
+ elsif $rest_auth_keys_are_new
+ puts "We've create a new site key in #{site_keys_file}. If you have existing"
+ puts "user accounts their passwords will no longer work (see README). As always,"
+ puts "keep this file safe but don't post it in public."
+ else
+ puts "We've reused the existing site key in #{site_keys_file}. As always,"
+ puts "keep this file safe but don't post it in public."
+ end
+ puts
+ puts ("-" * 70)
+ when "destroy"
+ puts
+ puts ("-" * 70)
+ puts
+ puts "Thanks for using restful_authentication"
+ puts
+ puts "Don't forget to comment out the observer line in environment.rb"
+ puts " (This was optional so it may not even be there)"
+ puts " # config.active_record.observers = :#{file_name}_observer"
+ puts
+ puts ("-" * 70)
+ puts
+ else
+ puts "Didn't understand the action '#{action}' -- you might have missed the 'after running me' instructions."
+ end
+
+ #
+ # Do the thing
+ #
+ recorded_session
+ end
+
+ def has_rspec?
+ spec_dir = File.join(RAILS_ROOT, 'spec')
+ options[:rspec] ||= (File.exist?(spec_dir) && File.directory?(spec_dir)) unless (options[:rspec] == false)
+ end
+
+ #
+ # !! These must match the corresponding routines in by_password.rb !!
+ #
+ def secure_digest(*args)
+ Digest::SHA1.hexdigest(args.flatten.join('--'))
+ end
+ def make_token
+ secure_digest(Time.now, (1..10).map{ rand.to_s })
+ end
+ def password_digest(password, salt)
+ digest = $rest_auth_site_key_from_generator
+ $rest_auth_digest_stretches_from_generator.times do
+ digest = secure_digest(digest, salt, password, $rest_auth_site_key_from_generator)
+ end
+ digest
+ end
+
+ #
+ # Try to be idempotent:
+ # pull in the existing site key if any,
+ # seed it with reasonable defaults otherwise
+ #
+ def load_or_initialize_site_keys
+ case
+ when defined? REST_AUTH_SITE_KEY
+ if (options[:old_passwords]) && ((! REST_AUTH_SITE_KEY.blank?) || (REST_AUTH_DIGEST_STRETCHES != 1))
+ raise "You have a site key, but --old-passwords will overwrite it. If this is really what you want, move the file #{site_keys_file} and re-run."
+ end
+ $rest_auth_site_key_from_generator = REST_AUTH_SITE_KEY
+ $rest_auth_digest_stretches_from_generator = REST_AUTH_DIGEST_STRETCHES
+ when options[:old_passwords]
+ $rest_auth_site_key_from_generator = nil
+ $rest_auth_digest_stretches_from_generator = 1
+ $rest_auth_keys_are_new = true
+ else
+ $rest_auth_site_key_from_generator = make_token
+ $rest_auth_digest_stretches_from_generator = 10
+ $rest_auth_keys_are_new = true
+ end
+ end
+ def site_keys_file
+ File.join("config", "initializers", "site_keys.rb")
+ end
+
+protected
+ # Override with your own usage banner.
+ def banner
+ "Usage: #{$0} authenticated ModelName [ControllerName]"
+ end
+
+ def add_options!(opt)
+ opt.separator ''
+ opt.separator 'Options:'
+ opt.on("--skip-migration",
+ "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
+ opt.on("--include-activation",
+ "Generate signup 'activation code' confirmation via email") { |v| options[:include_activation] = true }
+ opt.on("--stateful",
+ "Use acts_as_state_machine. Assumes --include-activation") { |v| options[:include_activation] = options[:stateful] = true }
+ opt.on("--aasm",
+ "Use (gem) aasm. Assumes --include-activation") { |v| options[:include_activation] = options[:stateful] = options[:aasm] = true }
+ opt.on("--rspec",
+ "Force rspec mode (checks for RAILS_ROOT/spec by default)") { |v| options[:rspec] = true }
+ opt.on("--no-rspec",
+ "Force test (not RSpec mode") { |v| options[:rspec] = false }
+ opt.on("--skip-routes",
+ "Don't generate a resource line in config/routes.rb") { |v| options[:skip_routes] = v }
+ opt.on("--old-passwords",
+ "Use the older password encryption scheme (see README)") { |v| options[:old_passwords] = v }
+ opt.on("--dump-generator-attrs",
+ "(generator debug helper)") { |v| options[:dump_generator_attribute_names] = v }
+ end
+
+ def dump_generator_attribute_names
+ generator_attribute_names = [
+ :table_name,
+ :file_name,
+ :class_name,
+ :controller_name,
+ :controller_class_path,
+ :controller_file_path,
+ :controller_class_nesting,
+ :controller_class_nesting_depth,
+ :controller_class_name,
+ :controller_singular_name,
+ :controller_plural_name,
+ :controller_routing_name, # new_session_path
+ :controller_routing_path, # /session/new
+ :controller_controller_name, # sessions
+ :controller_file_name,
+ :controller_table_name, :controller_plural_name,
+ :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,
+ :model_controller_routing_name, # new_user_path
+ :model_controller_routing_path, # /users/new
+ :model_controller_controller_name, # users
+ :model_controller_file_name, :model_controller_singular_name,
+ :model_controller_table_name, :model_controller_plural_name,
+ ]
+ generator_attribute_names.each do |attr|
+ puts "%-40s %s" % ["#{attr}:", self.send(attr)] # instance_variable_get("@#{attr.to_s}"
+ end
+
+ end
+end
+
+# ./script/generate authenticated FoonParent::Foon SporkParent::Spork -p --force --rspec --dump-generator-attrs
+# table_name: foon_parent_foons
+# file_name: foon
+# class_name: FoonParent::Foon
+# controller_name: SporkParent::Sporks
+# controller_class_path: spork_parent
+# controller_file_path: spork_parent/sporks
+# controller_class_nesting: SporkParent
+# controller_class_nesting_depth: 1
+# controller_class_name: SporkParent::Sporks
+# controller_singular_name: spork
+# controller_plural_name: sporks
+# controller_routing_name: spork
+# controller_routing_path: spork_parent/spork
+# controller_controller_name: sporks
+# controller_file_name: sporks
+# controller_table_name: sporks
+# controller_plural_name: sporks
+# model_controller_name: FoonParent::Foons
+# model_controller_class_path: foon_parent
+# model_controller_file_path: foon_parent/foons
+# model_controller_class_nesting: FoonParent
+# model_controller_class_nesting_depth: 1
+# model_controller_class_name: FoonParent::Foons
+# model_controller_singular_name: foons
+# model_controller_plural_name: foons
+# model_controller_routing_name: foon_parent_foons
+# model_controller_routing_path: foon_parent/foons
+# model_controller_controller_name: foons
+# model_controller_file_name: foons
+# model_controller_singular_name: foons
+# model_controller_table_name: foons
+# model_controller_plural_name: foons
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/lib/insert_routes.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/lib/insert_routes.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/lib/insert_routes.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,50 @@
+Rails::Generator::Commands::Create.class_eval do
+ def route_resource(*resources)
+ resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
+
+ logger.route "map.resource #{resource_list}"
+ unless options[:pretend]
+ gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
+ "#{match}\n map.resource #{resource_list}\n"
+ end
+ end
+ end
+
+ def route_name(name, path, options = {})
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
+
+ logger.route "map.#{name} '#{path}', :controller => '#{options[:controller]}', :action => '#{options[:action]}'"
+ unless options[:pretend]
+ gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
+ "#{match}\n map.#{name} '#{path}', :controller => '#{options[:controller]}', :action => '#{options[:action]}'"
+ end
+ end
+ end
+end
+
+Rails::Generator::Commands::Destroy.class_eval do
+ def route_resource(*resources)
+ resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+ look_for = "\n map.resource #{resource_list}\n"
+ logger.route "map.resource #{resource_list}"
+ gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
+ end
+
+ def route_name(name, path, options = {})
+ look_for = "\n map.#{name} '#{path}', :controller => '#{options[:controller]}', :action => '#{options[:action]}'"
+ logger.route "map.#{name} '#{path}', :controller => '#{options[:controller]}', :action => '#{options[:action]}'"
+ gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
+ end
+end
+
+Rails::Generator::Commands::List.class_eval do
+ def route_resource(*resources)
+ resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+ logger.route "map.resource #{resource_list}"
+ end
+
+ def route_name(name, path, options = {})
+ logger.route "map.#{name} '#{path}', :controller => '{options[:controller]}', :action => '#{options[:action]}'"
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/_model_partial.html.erb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/_model_partial.html.erb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/_model_partial.html.erb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,8 @@
+<%% if logged_in? -%>
+ <div id="<%= file_name %>-bar-greeting">Logged in as <%%= link_to_current_<%= file_name %> :content_method => :login %></div>
+ <div id="<%= file_name %>-bar-action" >(<%%= link_to "log out", logout_path, { :title => "Log out" } %>)</div>
+<%% else -%>
+ <div id="<%= file_name %>-bar-greeting"><%%= abbr_tag_with_IP 'Not logged in', :style => 'border: none;' %></div>
+ <div id="<%= file_name %>-bar-action" ><%%= link_to "Log in", login_path, { :title => "Log in" } %> /
+ <%%= link_to "Sign up", signup_path, { :title => "Create an account" } %></div>
+<%% end -%>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/activation.html.erb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/activation.html.erb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/activation.html.erb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,3 @@
+<%%=h @<%= file_name %>.login %>, your account has been activated. Welcome aboard!
+
+ <%%=h @url %>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_system.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_system.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_system.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,187 @@
+module AuthenticatedSystem
+ protected
+ # Returns true or false if the <%= file_name %> is logged in.
+ # Preloads @current_<%= file_name %> with the <%= file_name %> model if they're logged in.
+ def logged_in?
+ !!current_<%= file_name %>
+ end
+
+ # Accesses the current <%= file_name %> from the session.
+ # Future calls avoid the database because nil is not equal to false.
+ def current_<%= file_name %>
+ @current_<%= file_name %> ||= (login_from_session || login_from_basic_auth || login_from_cookie) unless @current_<%= file_name %> == false
+ end
+
+ # Store the given <%= file_name %> id in the session.
+ def current_<%= file_name %>=(new_<%= file_name %>)
+ session[:<%= file_name %>_id] = new_<%= file_name %> ? new_<%= file_name %>.id : nil
+ @current_<%= file_name %> = new_<%= file_name %> || false
+ 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 authorized?
+ # current_<%= file_name %>.login != "bob"
+ # end
+ #
+ def authorized?(action=nil, resource=nil, *args)
+ logged_in?
+ 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
+ authorized? || 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 |format|
+ format.html do
+ store_location
+ redirect_to new_<%= controller_routing_name %>_path
+ end
+ # format.any doesn't work in rails version < http://dev.rubyonrails.org/changeset/8987
+ # you may want to change format.any to e.g. format.any(:js, :xml)
+ format.any do
+ request_http_basic_authentication 'Web Password'
+ end
+ end
+ 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. Set an appropriately modified
+ # after_filter :store_location, :only => [:index, :new, :show, :edit]
+ # for any controller you want to be bounce-backable.
+ def redirect_back_or_default(default)
+ redirect_to(session[:return_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?, :authorized? if base.respond_to? :helper_method
+ end
+
+ #
+ # Login
+ #
+
+ # Called from #current_<%= file_name %>. First attempt to login by the <%= file_name %> id stored in the session.
+ def login_from_session
+ self.current_<%= file_name %> = <%= class_name %>.find_by_id(session[:<%= file_name %>_id]) if session[:<%= file_name %>_id]
+ end
+
+ # Called from #current_<%= file_name %>. Now, attempt to login by basic authentication information.
+ def login_from_basic_auth
+ authenticate_with_http_basic do |login, password|
+ self.current_<%= file_name %> = <%= class_name %>.authenticate(login, password)
+ end
+ end
+
+ #
+ # Logout
+ #
+
+ # Called from #current_<%= file_name %>. Finaly, attempt to login by an expiring token in the cookie.
+ # for the paranoid: we _should_ be storing <%= file_name %>_token = hash(cookie_token, request IP)
+ def login_from_cookie
+ <%= file_name %> = cookies[:auth_token] && <%= class_name %>.find_by_remember_token(cookies[:auth_token])
+ if <%= file_name %> && <%= file_name %>.remember_token?
+ self.current_<%= file_name %> = <%= file_name %>
+ handle_remember_cookie! false # freshen cookie token (keeping date)
+ self.current_<%= file_name %>
+ end
+ end
+
+ # This is ususally what you want; resetting the session willy-nilly wreaks
+ # havoc with forgery protection, and is only strictly necessary on login.
+ # However, **all session state variables should be unset here**.
+ def logout_keeping_session!
+ # Kill server-side auth cookie
+ @current_<%= file_name %>.forget_me if @current_<%= file_name %>.is_a? <%= class_name %>
+ @current_<%= file_name %> = false # not logged in, and don't do it for me
+ kill_remember_cookie! # Kill client-side auth cookie
+ session[:<%= file_name %>_id] = nil # keeps the session but kill our variable
+ # explicitly kill any other session variables you set
+ end
+
+ # The session should only be reset at the tail end of a form POST --
+ # otherwise the request forgery protection fails. It's only really necessary
+ # when you cross quarantine (logged-out to logged-in).
+ def logout_killing_session!
+ logout_keeping_session!
+ reset_session
+ end
+
+ #
+ # Remember_me Tokens
+ #
+ # Cookies shouldn't be allowed to persist past their freshness date,
+ # and they should be changed at each login
+
+ # Cookies shouldn't be allowed to persist past their freshness date,
+ # and they should be changed at each login
+
+ def valid_remember_cookie?
+ return nil unless @current_<%= file_name %>
+ (@current_<%= file_name %>.remember_token?) &&
+ (cookies[:auth_token] == @current_<%= file_name %>.remember_token)
+ end
+
+ # Refresh the cookie auth token if it exists, create it otherwise
+ def handle_remember_cookie! new_cookie_flag
+ return unless @current_<%= file_name %>
+ case
+ when valid_remember_cookie? then @current_<%= file_name %>.refresh_token # keeping same expiry date
+ when new_cookie_flag then @current_<%= file_name %>.remember_me
+ else @current_<%= file_name %>.forget_me
+ end
+ send_remember_cookie!
+ end
+
+ def kill_remember_cookie!
+ cookies.delete :auth_token
+ end
+
+ def send_remember_cookie!
+ cookies[:auth_token] = {
+ :value => @current_<%= file_name %>.remember_token,
+ :expires => @current_<%= file_name %>.remember_token_expires_at }
+ end
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_test_helper.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_test_helper.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/authenticated_test_helper.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,22 @@
+module AuthenticatedTestHelper
+ # Sets the current <%= file_name %> in the session from the <%= file_name %> fixtures.
+ def login_as(<%= file_name %>)
+ @request.session[:<%= file_name %>_id] = <%= file_name %> ? <%= table_name %>(<%= file_name %>).id : nil
+ end
+
+ def authorize_as(<%= file_name %>)
+ @request.env["HTTP_AUTHORIZATION"] = <%= file_name %> ? ActionController::HttpAuthentication::Basic.encode_credentials(<%= table_name %>(<%= file_name %>).login, 'monkey') : nil
+ end
+
+<% if options[:rspec] -%>
+ # rspec
+ def mock_<%= file_name %>
+ <%= file_name %> = mock_model(<%= class_name %>, :id => 1,
+ :login => 'user_name',
+ :name => 'U. Surname',
+ :to_xml => "<%= class_name %>-in-XML", :to_json => "<%= class_name %>-in-JSON",
+ :errors => [])
+ <%= file_name %>
+ end
+<% end -%>
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/controller.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/controller.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/controller.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,43 @@
+# 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
+
+ # render new.rhtml
+ def new
+ end
+
+ def create
+ logout_keeping_session!
+ <%= file_name %> = <%= class_name %>.authenticate(params[:login], params[:password])
+ if <%= file_name %>
+ # Protects against session fixation attacks, causes request forgery
+ # protection if user resubmits an earlier form using back
+ # button. Uncomment if you understand the tradeoffs.
+ # reset_session
+ self.current_<%= file_name %> = <%= file_name %>
+ new_cookie_flag = (params[:remember_me] == "1")
+ handle_remember_cookie! new_cookie_flag
+ redirect_back_or_default('/')
+ flash[:notice] = "Logged in successfully"
+ else
+ note_failed_signin
+ @login = params[:login]
+ @remember_me = params[:remember_me]
+ render :action => 'new'
+ end
+ end
+
+ def destroy
+ logout_killing_session!
+ flash[:notice] = "You have been logged out."
+ redirect_back_or_default('/')
+ end
+
+protected
+ # Track failed login attempts
+ def note_failed_signin
+ flash[:error] = "Couldn't log you in as '#{params[:login]}'"
+ logger.warn "Failed login for '#{params[:login]}' from #{request.remote_ip} at #{Time.now.utc}"
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/helper.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/helper.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/helper.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,2 @@
+module <%= controller_class_name %>Helper
+end
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/login.html.erb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/login.html.erb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/login.html.erb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,16 @@
+<h1>Log In</h1>
+
+<%% form_tag <%= controller_routing_name %>_path do -%>
+<p><label for="login">Login</label><br/>
+<%%= text_field_tag 'login', @login %></p>
+
+<p><label for="password">Password</label><br/>
+<%%= password_field_tag 'password', nil %></p>
+
+<!-- Uncomment this if you want this functionality
+<p><label for="remember_me">Remember me:</label>
+<%%= check_box_tag 'remember_me', '1', @remember_me %></p>
+-->
+
+<p><%%= submit_tag 'Log in' %></p>
+<%% end -%>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/mailer.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/mailer.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/mailer.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,25 @@
+class <%= class_name %>Mailer < ActionMailer::Base
+ def signup_notification(<%= file_name %>)
+ setup_email(<%= file_name %>)
+ @subject += 'Please activate your new account'
+ <% if options[:include_activation] %>
+ @body[:url] = "http://YOURSITE/activate/#{<%= file_name %>.activation_code}"
+ <% else %>
+ @body[:url] = "http://YOURSITE/login/" <% end %>
+ end
+
+ def activation(<%= file_name %>)
+ setup_email(<%= file_name %>)
+ @subject += 'Your account has been activated!'
+ @body[:url] = "http://YOURSITE/"
+ end
+
+ protected
+ def setup_email(<%= file_name %>)
+ @recipients = "#{<%= file_name %>.email}"
+ @from = "ADMINEMAIL"
+ @subject = "[YOURSITE] "
+ @sent_on = Time.now
+ @body[:<%= file_name %>] = <%= file_name %>
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/migration.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/migration.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/migration.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,26 @@
+class <%= migration_name %> < ActiveRecord::Migration
+ def self.up
+ create_table "<%= table_name %>", :force => true do |t|
+ t.column :login, :string, :limit => 40
+ t.column :name, :string, :limit => 100, :default => '', :null => true
+ t.column :email, :string, :limit => 100
+ 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, :limit => 40
+ t.column :remember_token_expires_at, :datetime
+<% if options[:include_activation] -%>
+ t.column :activation_code, :string, :limit => 40
+ t.column :activated_at, :datetime<% end %>
+<% if options[:stateful] -%>
+ t.column :state, :string, :null => :no, :default => 'passive'
+ t.column :deleted_at, :datetime<% end %>
+ end
+ add_index :<%= table_name %>, :login, :unique => true
+ end
+
+ def self.down
+ drop_table "<%= table_name %>"
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,69 @@
+require 'digest/sha1'
+
+class <%= class_name %> < ActiveRecord::Base
+ include Authentication
+ include Authentication::ByPassword
+ include Authentication::ByCookieToken
+<% if options[:aasm] -%>
+ include Authorization::AasmRoles
+<% elsif options[:stateful] -%>
+ include Authorization::StatefulRoles<% end %>
+ validates_presence_of :login
+ validates_length_of :login, :within => 3..40
+ validates_uniqueness_of :login, :case_sensitive => false
+ validates_format_of :login, :with => RE_LOGIN_OK, :message => MSG_LOGIN_BAD
+
+ validates_format_of :name, :with => RE_NAME_OK, :message => MSG_NAME_BAD, :allow_nil => true
+ validates_length_of :name, :maximum => 100
+
+ validates_presence_of :email
+ validates_length_of :email, :within => 6..100 #r at a.wk
+ validates_uniqueness_of :email, :case_sensitive => false
+ validates_format_of :email, :with => RE_EMAIL_OK, :message => MSG_EMAIL_BAD
+
+ <% if options[:include_activation] && !options[:stateful] %>before_create :make_activation_code <% end %>
+
+ # HACK HACK HACK -- how to do attr_accessible from here?
+ # prevents a user from submitting a crafted form that bypasses activation
+ # anything else you want your user to change should be added here.
+ attr_accessible :login, :email, :name, :password, :password_confirmation
+
+<% if options[:include_activation] && !options[:stateful] %>
+ # Activates the user in the database.
+ def activate!
+ @activated = true
+ self.activated_at = Time.now.utc
+ self.activation_code = nil
+ save(false)
+ end
+
+ def active?
+ # the existence of an activation code means they have not activated yet
+ activation_code.nil?
+ end<% end %>
+
+ # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
+ #
+ # uff. this is really an authorization, not authentication routine.
+ # We really need a Dispatch Chain here or something.
+ # This will also let us return a human error message.
+ #
+ def self.authenticate(login, password)
+ u = <% if options[:stateful] %>find_in_state :first, :active, :conditions => {:login => login}<%
+ elsif options[:include_activation] %>find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login]<%
+ else %>find_by_login(login)<% end %> # need to get the salt
+ u && u.authenticated?(password) ? u : nil
+ end
+
+ protected
+
+<% if options[:include_activation] -%>
+ def make_activation_code
+ <% if options[:stateful] -%>
+ self.deleted_at = nil
+ <% end -%>
+ self.activation_code = self.class.make_token
+ end
+<% end %>
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_controller.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_controller.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_controller.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,86 @@
+class <%= model_controller_class_name %>Controller < ApplicationController
+ # Be sure to include AuthenticationSystem in Application Controller instead
+ include AuthenticatedSystem
+ <% if options[:stateful] %>
+ # Protect these actions behind an admin login
+ # before_filter :admin_required, :only => [:suspend, :unsuspend, :destroy, :purge]
+ before_filter :find_<%= file_name %>, :only => [:suspend, :unsuspend, :destroy, :purge]
+ <% end %>
+
+ # render new.rhtml
+ def new
+ @<%= file_name %> = <%= class_name %>.new
+ end
+
+ def create
+ logout_keeping_session!
+ @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
+<% if options[:stateful] -%>
+ @<%= file_name %>.register! if @<%= file_name %> && @<%= file_name %>.valid?
+ success = @<%= file_name %> && @<%= file_name %>.valid?
+<% else -%>
+ success = @<%= file_name %> && @<%= file_name %>.save
+<% end -%>
+ if success && @<%= file_name %>.errors.empty?
+ <% if !options[:include_activation] -%>
+ # Protects against session fixation attacks, causes request forgery
+ # protection if visitor resubmits an earlier form using back
+ # button. Uncomment if you understand the tradeoffs.
+ # reset session
+ self.current_<%= file_name %> = @<%= file_name %> # !! now logged in
+ <% end -%>
+ redirect_back_or_default('/')
+ flash[:notice] = "Thanks for signing up! We're sending you an email with your activation code."
+ else
+ flash[:error] = "We couldn't set up that account, sorry. Please try again, or contact an admin (link is above)."
+ render :action => 'new'
+ end
+ end
+<% if options[:include_activation] %>
+ def activate
+ logout_keeping_session!
+ <%= file_name %> = <%= class_name %>.find_by_activation_code(params[:activation_code]) unless params[:activation_code].blank?
+ case
+ when (!params[:activation_code].blank?) && <%= file_name %> && !<%= file_name %>.active?
+ <%= file_name %>.activate!
+ flash[:notice] = "Signup complete! Please sign in to continue."
+ redirect_to '/login'
+ when params[:activation_code].blank?
+ flash[:error] = "The activation code was missing. Please follow the URL from your email."
+ redirect_back_or_default('/')
+ else
+ flash[:error] = "We couldn't find a <%= file_name %> with that activation code -- check your email? Or maybe you've already activated -- try signing in."
+ redirect_back_or_default('/')
+ end
+ end
+<% end %><% if options[:stateful] %>
+ def suspend
+ @<%= file_name %>.suspend!
+ redirect_to <%= model_controller_routing_name %>_path
+ end
+
+ def unsuspend
+ @<%= file_name %>.unsuspend!
+ redirect_to <%= model_controller_routing_name %>_path
+ end
+
+ def destroy
+ @<%= file_name %>.delete!
+ redirect_to <%= model_controller_routing_name %>_path
+ end
+
+ def purge
+ @<%= file_name %>.destroy
+ redirect_to <%= model_controller_routing_name %>_path
+ end
+
+ # There's no page here to update or destroy a <%= file_name %>. If you add those, be
+ # smart -- make sure you check that the visitor is authorized to do so, that they
+ # supply their old password along with a new one to update it, etc.
+
+protected
+ def find_<%= file_name %>
+ @<%= file_name %> = <%= class_name %>.find(params[:id])
+ end
+<% end -%>
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,93 @@
+module <%= model_controller_class_name %>Helper
+
+ #
+ # Use this to wrap view elements that the user can't access.
+ # !! Note: this is an *interface*, not *security* feature !!
+ # You need to do all access control at the controller level.
+ #
+ # Example:
+ # <%%= if_authorized?(:index, User) do link_to('List all users', users_path) end %> |
+ # <%%= if_authorized?(:edit, @user) do link_to('Edit this user', edit_user_path) end %> |
+ # <%%= if_authorized?(:destroy, @user) do link_to 'Destroy', @user, :confirm => 'Are you sure?', :method => :delete end %>
+ #
+ #
+ def if_authorized?(action, resource, &block)
+ if authorized?(action, resource)
+ yield action, resource
+ end
+ end
+
+ #
+ # Link to user's page ('<%= table_name %>/1')
+ #
+ # By default, their login is used as link text and link title (tooltip)
+ #
+ # Takes options
+ # * :content_text => 'Content text in place of <%= file_name %>.login', escaped with
+ # the standard h() function.
+ # * :content_method => :<%= file_name %>_instance_method_to_call_for_content_text
+ # * :title_method => :<%= file_name %>_instance_method_to_call_for_title_attribute
+ # * as well as link_to()'s standard options
+ #
+ # Examples:
+ # link_to_<%= file_name %> @<%= file_name %>
+ # # => <a href="/<%= table_name %>/3" title="barmy">barmy</a>
+ #
+ # # if you've added a .name attribute:
+ # content_tag :span, :class => :vcard do
+ # (link_to_<%= file_name %> <%= file_name %>, :class => 'fn n', :title_method => :login, :content_method => :name) +
+ # ': ' + (content_tag :span, <%= file_name %>.email, :class => 'email')
+ # end
+ # # => <span class="vcard"><a href="/<%= table_name %>/3" title="barmy" class="fn n">Cyril Fotheringay-Phipps</a>: <span class="email">barmy at blandings.com</span></span>
+ #
+ # link_to_<%= file_name %> @<%= file_name %>, :content_text => 'Your user page'
+ # # => <a href="/<%= table_name %>/3" title="barmy" class="nickname">Your user page</a>
+ #
+ def link_to_<%= file_name %>(<%= file_name %>, options={})
+ raise "Invalid <%= file_name %>" unless <%= file_name %>
+ options.reverse_merge! :content_method => :login, :title_method => :login, :class => :nickname
+ content_text = options.delete(:content_text)
+ content_text ||= <%= file_name %>.send(options.delete(:content_method))
+ options[:title] ||= <%= file_name %>.send(options.delete(:title_method))
+ link_to h(content_text), <%= model_controller_routing_name.singularize %>_path(<%= file_name %>), options
+ end
+
+ #
+ # Link to login page using remote ip address as link content
+ #
+ # The :title (and thus, tooltip) is set to the IP address
+ #
+ # Examples:
+ # link_to_login_with_IP
+ # # => <a href="/login" title="169.69.69.69">169.69.69.69</a>
+ #
+ # link_to_login_with_IP :content_text => 'not signed in'
+ # # => <a href="/login" title="169.69.69.69">not signed in</a>
+ #
+ def link_to_login_with_IP content_text=nil, options={}
+ ip_addr = request.remote_ip
+ content_text ||= ip_addr
+ options.reverse_merge! :title => ip_addr
+ if tag = options.delete(:tag)
+ content_tag tag, h(content_text), options
+ else
+ link_to h(content_text), login_path, options
+ end
+ end
+
+ #
+ # Link to the current user's page (using link_to_<%= file_name %>) or to the login page
+ # (using link_to_login_with_IP).
+ #
+ def link_to_current_<%= file_name %>(options={})
+ if current_<%= file_name %>
+ link_to_<%= file_name %> current_<%= file_name %>, options
+ else
+ content_text = options.delete(:content_text) || 'not signed in'
+ # kill ignored options from link_to_<%= file_name %>
+ [:content_method, :title_method].each{|opt| options.delete(opt)}
+ link_to_login_with_IP content_text, options
+ end
+ end
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/model_helper_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,158 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+include ApplicationHelper
+include <%= model_controller_class_name %>Helper
+
+describe "<%= model_controller_class_name %>Helper.link_to_<%= file_name %>" do
+ before do
+ @<%= file_name %> = <%= class_name %>.new({
+ :name => '<%= class_name %> Name',
+ :login => '<%= file_name %>_name',
+ })
+ @<%= file_name %>.id = 1 # set non-attr_accessible specifically
+ end
+
+ it "should give an error on a nil <%= file_name %>" do
+ lambda { link_to_<%= file_name %>(nil) }.should raise_error('Invalid <%= file_name %>')
+ end
+
+ it "should link to the given <%= file_name %>" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a[href='/<%= table_name %>/1']")
+ end
+
+ it "should use given link text if :content_text is specified" do
+ link_to_<%= file_name %>(@<%= file_name %>, :content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+
+ it "should use the login as link text with no :content_method specified" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a", '<%= file_name %>_name')
+ end
+
+ it "should use the name as link text with :content_method => :name" do
+ link_to_<%= file_name %>(@<%= file_name %>, :content_method => :name).should have_tag("a", '<%= class_name %> Name')
+ end
+
+ it "should use the login as title with no :title_method specified" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a[title='<%= file_name %>_name']")
+ end
+
+ it "should use the name as link title with :content_method => :name" do
+ link_to_<%= file_name %>(@<%= file_name %>, :title_method => :name).should have_tag("a[title='<%= class_name %> Name']")
+ end
+
+ it "should have nickname as a class by default" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a.nickname")
+ end
+
+ it "should take other classes and no longer have the nickname class" do
+ result = link_to_<%= file_name %>(@<%= file_name %>, :class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+end
+
+describe "<%= model_controller_class_name %>Helper.link_to_signin_with_IP" do
+ before do
+ end
+
+ it "should link to the signin_path" do
+ link_to_signin_with_IP().should have_tag("a[href='/signin']")
+ end
+
+ it "should use given link text if :content_text is specified" do
+ link_to_signin_with_IP(:content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+
+ it "should use the login as link text with no :content_method specified" do
+ link_to_signin_with_IP().should have_tag("a", '0.0.0.0')
+ end
+
+ it "should use the ip address as title" do
+ link_to_signin_with_IP().should have_tag("a[title='0.0.0.0']")
+ end
+
+ it "should by default be like school in summer and have no class" do
+ link_to_signin_with_IP().should_not have_tag("a.nickname")
+ end
+
+ it "should have some class if you tell it to" do
+ result = link_to_signin_with_IP(:class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+end
+
+describe "<%= model_controller_class_name %>Helper.link_to_current_<%= file_name %>, When logged in" do
+ fixtures :<%= table_name %>
+ include AuthenticatedTestHelper
+ before do
+ login_as(:quentin)
+ end
+
+ it "should link to the given <%= file_name %>" do
+ link_to_current_<%= file_name %>().should have_tag("a[href='/<%= table_name %>/1']")
+ end
+
+ it "should use given link text if :content_text is specified" do
+ link_to_current_user(:content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+
+ it "should use the login as link text with no :content_method specified" do
+ link_to_current_user().should have_tag("a", 'quentin')
+ end
+
+ it "should use the name as link text with :content_method => :name" do
+ link_to_current_user(:content_method => :name).should have_tag("a", 'Quentin')
+ end
+
+ it "should use the login as title with no :title_method specified" do
+ link_to_current_user().should have_tag("a[title='quentin']")
+ end
+
+ it "should use the name as link title with :content_method => :name" do
+ link_to_current_user(:title_method => :name).should have_tag("a[title='Quentin']")
+ end
+
+ it "should have nickname as a class" do
+ link_to_current_user().should have_tag("a.nickname")
+ end
+
+ it "should take other classes and no longer have the nickname class" do
+ result = link_to_current_user(:class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+end
+
+
+
+describe "<%= model_controller_class_name %>Helper.link_to_current_user, When logged out" do
+ include AuthenticatedTestHelper
+ before do
+ end
+
+ it "should link to the signin_path" do
+ link_to_current_user().should have_tag("a[href='/signin']")
+ end
+
+ it "should use given link text if :content_text is specified" do
+ link_to_current_user(:content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+
+ it "should use the IP address as link text with no :content_method specified" do
+ link_to_current_user().should have_tag("a", '0.0.0.0')
+ end
+
+ it "should use the ip address as title" do
+ link_to_current_user().should have_tag("a[title='0.0.0.0']")
+ end
+
+ it "should by default be like school in summer and have no class" do
+ link_to_current_user().should_not have_tag("a.nickname")
+ end
+
+ it "should have some class if you tell it to" do
+ result = link_to_current_user(:class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/observer.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/observer.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/observer.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,11 @@
+class <%= class_name %>Observer < ActiveRecord::Observer
+ def after_create(<%= file_name %>)
+ <%= class_name %>Mailer.deliver_signup_notification(<%= file_name %>)
+ end
+
+ def after_save(<%= file_name %>)
+ <% if options[:include_activation] %>
+ <%= class_name %>Mailer.deliver_activation(<%= file_name %>) if <%= file_name %>.recently_activated?
+ <% end %>
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup.html.erb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup.html.erb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup.html.erb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,19 @@
+<h1>Sign up as a new user</h1>
+<%% @<%= file_name %>.password = @<%= file_name %>.password_confirmation = nil %>
+
+<%%= error_messages_for :<%= file_name %> %>
+<%% form_for :<%= file_name %>, :url => <%= model_controller_routing_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 -%>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup_notification.html.erb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup_notification.html.erb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/signup_notification.html.erb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,8 @@
+Your account has been created.
+
+ Username: <%%=h @<%= file_name %>.login %>
+ Password: <%%=h @<%= file_name %>.password %>
+
+Visit this url to activate your account:
+
+ <%%=h @url %>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/site_keys.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/site_keys.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/site_keys.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,38 @@
+
+# A Site key gives additional protection against a dictionary attack if your
+# DB is ever compromised. With no site key, we store
+# DB_password = hash(user_password, DB_user_salt)
+# If your database were to be compromised you'd be vulnerable to a dictionary
+# attack on all your stupid users' passwords. With a site key, we store
+# DB_password = hash(user_password, DB_user_salt, Code_site_key)
+# That means an attacker needs access to both your site's code *and* its
+# database to mount an "offline dictionary attack.":http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/web-authentication.html
+#
+# It's probably of minor importance, but recommended by best practices: 'defense
+# in depth'. Needless to say, if you upload this to github or the youtubes or
+# otherwise place it in public view you'll kinda defeat the point. Your users'
+# passwords are still secure, and the world won't end, but defense_in_depth -= 1.
+#
+# Please note: if you change this, all the passwords will be invalidated, so DO
+# keep it someplace secure. Use the random value given or type in the lyrics to
+# your favorite Jay-Z song or something; any moderately long, unpredictable text.
+REST_AUTH_SITE_KEY = '<%= $rest_auth_site_key_from_generator %>'
+
+# Repeated applications of the hash make brute force (even with a compromised
+# database and site key) harder, and scale with Moore's law.
+#
+# bq. "To squeeze the most security out of a limited-entropy password or
+# passphrase, we can use two techniques [salting and stretching]... that are
+# so simple and obvious that they should be used in every password system.
+# There is really no excuse not to use them." http://tinyurl.com/37lb73
+# Practical Security (Ferguson & Scheier) p350
+#
+# A modest 10 foldings (the default here) adds 3ms. This makes brute forcing 10
+# times harder, while reducing an app that otherwise serves 100 reqs/s to 78 signin
+# reqs/s, an app that does 10reqs/s to 9.7 reqs/s
+#
+# More:
+# * http://www.owasp.org/index.php/Hashing_Java
+# * "An Illustrated Guide to Cryptographic Hashes":http://www.unixwiz.net/techtips/iguide-crypto-hashes.html
+
+REST_AUTH_DIGEST_STRETCHES = <%= $rest_auth_digest_stretches_from_generator %>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/access_control_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/access_control_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/access_control_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,90 @@
+require File.dirname(__FILE__) + '<%= ('/..'*controller_class_nesting_depth) + '/../spec_helper' %>'
+ # Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead
+# Then, you can remove it from this and the units test.
+include AuthenticatedTestHelper
+
+#
+# A test controller with and without access controls
+#
+class AccessControlTestController < ApplicationController
+ before_filter :login_required, :only => :login_is_required
+ def login_is_required
+ respond_to do |format|
+ @foo = { 'success' => params[:format]||'no fmt given'}
+ format.html do render :text => "success" end
+ format.xml do render :xml => @foo, :status => :ok end
+ format.json do render :json => @foo, :status => :ok end
+ end
+ end
+ def login_not_required
+ respond_to do |format|
+ @foo = { 'success' => params[:format]||'no fmt given'}
+ format.html do render :text => "success" end
+ format.xml do render :xml => @foo, :status => :ok end
+ format.json do render :json => @foo, :status => :ok end
+ end
+ end
+end
+
+#
+# Access Control
+#
+
+ACCESS_CONTROL_FORMATS = [
+ ['', "success"],
+ ['xml', "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <success>xml</success>\n</hash>\n"],
+ ['json', "{\"success\": \"json\"}"],]
+ACCESS_CONTROL_AM_I_LOGGED_IN = [
+ [:i_am_logged_in, :quentin],
+ [:i_am_not_logged_in, nil],]
+ACCESS_CONTROL_IS_LOGIN_REQD = [
+ :login_not_required,
+ :login_is_required,]
+
+describe AccessControlTestController do
+ fixtures :<%= table_name %>
+ before do
+ # is there a better way to do this?
+ ActionController::Routing::Routes.add_route '/login_is_required', :controller => 'access_control_test', :action => 'login_is_required'
+ ActionController::Routing::Routes.add_route '/login_not_required', :controller => 'access_control_test', :action => 'login_not_required'
+ end
+
+ ACCESS_CONTROL_FORMATS.each do |format, success_text|
+ ACCESS_CONTROL_AM_I_LOGGED_IN.each do |logged_in_status, <%= file_name %>_login|
+ ACCESS_CONTROL_IS_LOGIN_REQD.each do |login_reqd_status|
+ describe "requesting #{format.blank? ? 'html' : format}; #{logged_in_status.to_s.humanize} and #{login_reqd_status.to_s.humanize}" do
+ before do
+ logout_keeping_session!
+ @<%= file_name %> = format.blank? ? login_as(<%= file_name %>_login) : authorize_as(<%= file_name %>_login)
+ get login_reqd_status.to_s, :format => format
+ end
+
+ if ((login_reqd_status == :login_not_required) ||
+ (login_reqd_status == :login_is_required && logged_in_status == :i_am_logged_in))
+ it "succeeds" do
+ response.should have_text(success_text)
+ response.code.to_s.should == '200'
+ end
+
+ elsif (login_reqd_status == :login_is_required && logged_in_status == :i_am_not_logged_in)
+ if ['html', ''].include? format
+ it "redirects me to the log in page" do
+ response.should redirect_to('/<%= controller_routing_path %>/new')
+ end
+ else
+ it "returns 'Access denied' and a 406 (Access Denied) status code" do
+ response.should have_text("HTTP Basic: Access denied.\n")
+ response.code.to_s.should == '401'
+ end
+ end
+
+ else
+ warn "Oops no case for #{format} and #{logged_in_status.to_s.humanize} and #{login_reqd_status.to_s.humanize}"
+ end
+ end # describe
+
+ end
+ end
+ end # cases
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/authenticated_system_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/authenticated_system_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/authenticated_system_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,101 @@
+require File.dirname(__FILE__) + '<%= ('/..'*controller_class_nesting_depth) + '/../spec_helper' %>'
+
+# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead
+# Then, you can remove it from this and the units test.
+include AuthenticatedTestHelper
+include AuthenticatedSystem
+
+describe <%= controller_class_name %>Controller do
+ fixtures :<%= table_name %>
+
+ before do
+ # FIXME -- <%= controller_file_name %> controller not testing xml logins
+ stub!(:authenticate_with_http_basic).and_return nil
+ end
+ describe "logout_killing_session!" do
+ before do
+ login_as :quentin
+ stub!(:reset_session)
+ end
+ it 'resets the session' do should_receive(:reset_session); logout_killing_session! end
+ it 'kills my auth_token cookie' do should_receive(:kill_remember_cookie!); logout_killing_session! end
+ it 'nils the current <%= file_name %>' do logout_killing_session!; current_<%= file_name %>.should be_nil end
+ it 'kills :<%= file_name %>_id session' do
+ session.stub!(:[]=)
+ session.should_receive(:[]=).with(:<%= file_name %>_id, nil).at_least(:once)
+ logout_killing_session!
+ end
+ it 'forgets me' do
+ current_<%= file_name %>.remember_me
+ current_<%= file_name %>.remember_token.should_not be_nil; current_<%= file_name %>.remember_token_expires_at.should_not be_nil
+ <%= class_name %>.find(1).remember_token.should_not be_nil; <%= class_name %>.find(1).remember_token_expires_at.should_not be_nil
+ logout_killing_session!
+ <%= class_name %>.find(1).remember_token.should be_nil; <%= class_name %>.find(1).remember_token_expires_at.should be_nil
+ end
+ end
+
+ describe "logout_keeping_session!" do
+ before do
+ login_as :quentin
+ stub!(:reset_session)
+ end
+ it 'does not reset the session' do should_not_receive(:reset_session); logout_keeping_session! end
+ it 'kills my auth_token cookie' do should_receive(:kill_remember_cookie!); logout_keeping_session! end
+ it 'nils the current <%= file_name %>' do logout_keeping_session!; current_<%= file_name %>.should be_nil end
+ it 'kills :<%= file_name %>_id session' do
+ session.stub!(:[]=)
+ session.should_receive(:[]=).with(:<%= file_name %>_id, nil).at_least(:once)
+ logout_keeping_session!
+ end
+ it 'forgets me' do
+ current_<%= file_name %>.remember_me
+ current_<%= file_name %>.remember_token.should_not be_nil; current_<%= file_name %>.remember_token_expires_at.should_not be_nil
+ <%= class_name %>.find(1).remember_token.should_not be_nil; <%= class_name %>.find(1).remember_token_expires_at.should_not be_nil
+ logout_keeping_session!
+ <%= class_name %>.find(1).remember_token.should be_nil; <%= class_name %>.find(1).remember_token_expires_at.should be_nil
+ end
+ end
+
+ describe 'When logged out' do
+ it "should not be authorized?" do
+ authorized?().should be_false
+ end
+ end
+
+ #
+ # Cookie Login
+ #
+ describe "Logging in by cookie" do
+ def set_remember_token token, time
+ @<%= file_name %>[:remember_token] = token;
+ @<%= file_name %>[:remember_token_expires_at] = time
+ @<%= file_name %>.save!
+ end
+ before do
+ @<%= file_name %> = <%= class_name %>.find(:first);
+ set_remember_token 'hello!', 5.minutes.from_now
+ end
+ it 'logs in with cookie' do
+ stub!(:cookies).and_return({ :auth_token => 'hello!' })
+ logged_in?.should be_true
+ end
+
+ it 'fails cookie login with bad cookie' do
+ should_receive(:cookies).at_least(:once).and_return({ :auth_token => 'i_haxxor_joo' })
+ logged_in?.should_not be_true
+ end
+
+ it 'fails cookie login with no cookie' do
+ set_remember_token nil, nil
+ should_receive(:cookies).at_least(:once).and_return({ })
+ logged_in?.should_not be_true
+ end
+
+ it 'fails expired cookie login' do
+ set_remember_token 'hello!', 5.minutes.ago
+ stub!(:cookies).and_return({ :auth_token => 'hello!' })
+ logged_in?.should_not be_true
+ end
+ end
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/sessions_controller_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/sessions_controller_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/sessions_controller_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,139 @@
+require File.dirname(__FILE__) + '<%= ('/..'*controller_class_nesting_depth) + '/../spec_helper' %>'
+
+# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead
+# Then, you can remove it from this and the units test.
+include AuthenticatedTestHelper
+
+describe <%= controller_class_name %>Controller do
+ fixtures :<%= table_name %>
+ before do
+ @<%= file_name %> = mock_<%= file_name %>
+ @login_params = { :login => 'quentin', :password => 'test' }
+ <%= class_name %>.stub!(:authenticate).with(@login_params[:login], @login_params[:password]).and_return(@<%= file_name %>)
+ end
+ def do_create
+ post :create, @login_params
+ end
+ describe "on successful login," do
+ [ [:nil, nil, nil],
+ [:expired, 'valid_token', 15.minutes.ago],
+ [:different, 'i_haxxor_joo', 15.minutes.from_now],
+ [:valid, 'valid_token', 15.minutes.from_now]
+ ].each do |has_request_token, token_value, token_expiry|
+ [ true, false ].each do |want_remember_me|
+ describe "my request cookie token is #{has_request_token.to_s}," do
+ describe "and ask #{want_remember_me ? 'to' : 'not to'} be remembered" do
+ before do
+ @ccookies = mock('cookies')
+ controller.stub!(:cookies).and_return(@ccookies)
+ @ccookies.stub!(:[]).with(:auth_token).and_return(token_value)
+ @ccookies.stub!(:delete).with(:auth_token)
+ @ccookies.stub!(:[]=)
+ @<%= file_name %>.stub!(:remember_me)
+ @<%= file_name %>.stub!(:refresh_token)
+ @<%= file_name %>.stub!(:forget_me)
+ @<%= file_name %>.stub!(:remember_token).and_return(token_value)
+ @<%= file_name %>.stub!(:remember_token_expires_at).and_return(token_expiry)
+ @<%= file_name %>.stub!(:remember_token?).and_return(has_request_token == :valid)
+ if want_remember_me
+ @login_params[:remember_me] = '1'
+ else
+ @login_params[:remember_me] = '0'
+ end
+ end
+ it "kills existing login" do controller.should_receive(:logout_keeping_session!); do_create; end
+ it "authorizes me" do do_create; controller.authorized?().should be_true; end
+ it "logs me in" do do_create; controller.logged_in?().should be_true end
+ it "greets me nicely" do do_create; response.flash[:notice].should =~ /success/i end
+ it "sets/resets/expires cookie" do controller.should_receive(:handle_remember_cookie!).with(want_remember_me); do_create end
+ it "sends a cookie" do controller.should_receive(:send_remember_cookie!); do_create end
+ it 'redirects to the home page' do do_create; response.should redirect_to('/') end
+ it "does not reset my session" do controller.should_not_receive(:reset_session).and_return nil; do_create end # change if you uncomment the reset_session path
+ if (has_request_token == :valid)
+ it 'does not make new token' do @<%= file_name %>.should_not_receive(:remember_me); do_create end
+ it 'does refresh token' do @<%= file_name %>.should_receive(:refresh_token); do_create end
+ it "sets an auth cookie" do do_create; end
+ else
+ if want_remember_me
+ it 'makes a new token' do @<%= file_name %>.should_receive(:remember_me); do_create end
+ it "does not refresh token" do @<%= file_name %>.should_not_receive(:refresh_token); do_create end
+ it "sets an auth cookie" do do_create; end
+ else
+ it 'does not make new token' do @<%= file_name %>.should_not_receive(:remember_me); do_create end
+ it 'does not refresh token' do @<%= file_name %>.should_not_receive(:refresh_token); do_create end
+ it 'kills user token' do @<%= file_name %>.should_receive(:forget_me); do_create end
+ end
+ end
+ end # inner describe
+ end
+ end
+ end
+ end
+
+ describe "on failed login" do
+ before do
+ <%= class_name %>.should_receive(:authenticate).with(anything(), anything()).and_return(nil)
+ login_as :quentin
+ end
+ it 'logs out keeping session' do controller.should_receive(:logout_keeping_session!); do_create end
+ it 'flashes an error' do do_create; flash[:error].should =~ /Couldn't log you in as 'quentin'/ end
+ it 'renders the log in page' do do_create; response.should render_template('new') end
+ it "doesn't log me in" do do_create; controller.logged_in?().should == false end
+ it "doesn't send password back" do
+ @login_params[:password] = 'FROBNOZZ'
+ do_create
+ response.should_not have_text(/FROBNOZZ/i)
+ end
+ end
+
+ describe "on signout" do
+ def do_destroy
+ get :destroy
+ end
+ before do
+ login_as :quentin
+ end
+ it 'logs me out' do controller.should_receive(:logout_killing_session!); do_destroy end
+ it 'redirects me to the home page' do do_destroy; response.should be_redirect end
+ end
+
+end
+
+describe <%= controller_class_name %>Controller do
+ describe "route generation" do
+ it "should route the new <%= controller_controller_name %> action correctly" do
+ route_for(:controller => '<%= controller_controller_name %>', :action => 'new').should == "/login"
+ end
+ it "should route the create <%= controller_controller_name %> correctly" do
+ route_for(:controller => '<%= controller_controller_name %>', :action => 'create').should == "/<%= controller_routing_path %>"
+ end
+ it "should route the destroy <%= controller_controller_name %> action correctly" do
+ route_for(:controller => '<%= controller_controller_name %>', :action => 'destroy').should == "/logout"
+ end
+ end
+
+ describe "route recognition" do
+ it "should generate params from GET /login correctly" do
+ params_from(:get, '/login').should == {:controller => '<%= controller_controller_name %>', :action => 'new'}
+ end
+ it "should generate params from POST /<%= controller_routing_path %> correctly" do
+ params_from(:post, '/<%= controller_routing_path %>').should == {:controller => '<%= controller_controller_name %>', :action => 'create'}
+ end
+ it "should generate params from DELETE /<%= controller_routing_path %> correctly" do
+ params_from(:delete, '/logout').should == {:controller => '<%= controller_controller_name %>', :action => 'destroy'}
+ end
+ end
+
+ describe "named routing" do
+ before(:each) do
+ get :new
+ end
+ it "should route <%= controller_routing_name %>_path() correctly" do
+ <%= controller_routing_name %>_path().should == "/<%= controller_routing_path %>"
+ end
+ it "should route new_<%= controller_routing_name %>_path() correctly" do
+ new_<%= controller_routing_name %>_path().should == "/<%= controller_routing_path %>/new"
+ end
+ end
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/users_controller_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/users_controller_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/controllers/users_controller_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,198 @@
+require File.dirname(__FILE__) + '<%= ('/..'*model_controller_class_nesting_depth) + '/../spec_helper' %>'
+
+# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead
+# Then, you can remove it from this and the units test.
+include AuthenticatedTestHelper
+
+describe <%= model_controller_class_name %>Controller do
+ fixtures :<%= table_name %>
+
+ it 'allows signup' do
+ lambda do
+ create_<%= file_name %>
+ response.should be_redirect
+ end.should change(<%= class_name %>, :count).by(1)
+ end
+
+ <% if options[:stateful] %>
+ it 'signs up user in pending state' do
+ create_<%= file_name %>
+ assigns(:<%= file_name %>).reload
+ assigns(:<%= file_name %>).should be_pending
+ end<% end %>
+
+<% if options[:include_activation] -%>
+ it 'signs up user with activation code' do
+ create_<%= file_name %>
+ assigns(:<%= file_name %>).reload
+ assigns(:<%= file_name %>).activation_code.should_not be_nil
+ end<% end -%>
+
+ it 'requires login on signup' do
+ lambda do
+ create_<%= file_name %>(:login => nil)
+ assigns[:<%= file_name %>].errors.on(:login).should_not be_nil
+ response.should be_success
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ it 'requires password on signup' do
+ lambda do
+ create_<%= file_name %>(:password => nil)
+ assigns[:<%= file_name %>].errors.on(:password).should_not be_nil
+ response.should be_success
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ it 'requires password confirmation on signup' do
+ lambda do
+ create_<%= file_name %>(:password_confirmation => nil)
+ assigns[:<%= file_name %>].errors.on(:password_confirmation).should_not be_nil
+ response.should be_success
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ it 'requires email on signup' do
+ lambda do
+ create_<%= file_name %>(:email => nil)
+ assigns[:<%= file_name %>].errors.on(:email).should_not be_nil
+ response.should be_success
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ <% if options[:include_activation] %>
+ it 'activates user' do
+ <%= class_name %>.authenticate('aaron', 'monkey').should be_nil
+ get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
+ response.should redirect_to('/login')
+ flash[:notice].should_not be_nil
+ flash[:error ].should be_nil
+ <%= class_name %>.authenticate('aaron', 'monkey').should == <%= table_name %>(:aaron)
+ end
+
+ it 'does not activate user without key' do
+ get :activate
+ flash[:notice].should be_nil
+ flash[:error ].should_not be_nil
+ end
+
+ it 'does not activate user with blank key' do
+ get :activate, :activation_code => ''
+ flash[:notice].should be_nil
+ flash[:error ].should_not be_nil
+ end
+
+ it 'does not activate user with bogus key' do
+ get :activate, :activation_code => 'i_haxxor_joo'
+ flash[:notice].should be_nil
+ flash[:error ].should_not be_nil
+ end<% end %>
+
+ def create_<%= file_name %>(options = {})
+ post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire at example.com',
+ :password => 'quire69', :password_confirmation => 'quire69' }.merge(options)
+ end
+end
+
+describe <%= model_controller_class_name %>Controller do
+ describe "route generation" do
+ it "should route <%= model_controller_controller_name %>'s 'index' action correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'index').should == "/<%= model_controller_routing_path %>"
+ end
+
+ it "should route <%= model_controller_controller_name %>'s 'new' action correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'new').should == "/signup"
+ end
+
+ it "should route {:controller => '<%= model_controller_controller_name %>', :action => 'create'} correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'create').should == "/register"
+ end
+
+ it "should route <%= model_controller_controller_name %>'s 'show' action correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'show', :id => '1').should == "/<%= model_controller_routing_path %>/1"
+ end
+
+ it "should route <%= model_controller_controller_name %>'s 'edit' action correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'edit', :id => '1').should == "/<%= model_controller_routing_path %>/1/edit"
+ end
+
+ it "should route <%= model_controller_controller_name %>'s 'update' action correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'update', :id => '1').should == "/<%= model_controller_routing_path %>/1"
+ end
+
+ it "should route <%= model_controller_controller_name %>'s 'destroy' action correctly" do
+ route_for(:controller => '<%= model_controller_controller_name %>', :action => 'destroy', :id => '1').should == "/<%= model_controller_routing_path %>/1"
+ end
+ end
+
+ describe "route recognition" do
+ it "should generate params for <%= model_controller_controller_name %>'s index action from GET /<%= model_controller_routing_path %>" do
+ params_from(:get, '/<%= model_controller_routing_path %>').should == {:controller => '<%= model_controller_controller_name %>', :action => 'index'}
+ params_from(:get, '/<%= model_controller_routing_path %>.xml').should == {:controller => '<%= model_controller_controller_name %>', :action => 'index', :format => 'xml'}
+ params_from(:get, '/<%= model_controller_routing_path %>.json').should == {:controller => '<%= model_controller_controller_name %>', :action => 'index', :format => 'json'}
+ end
+
+ it "should generate params for <%= model_controller_controller_name %>'s new action from GET /<%= model_controller_routing_path %>" do
+ params_from(:get, '/<%= model_controller_routing_path %>/new').should == {:controller => '<%= model_controller_controller_name %>', :action => 'new'}
+ params_from(:get, '/<%= model_controller_routing_path %>/new.xml').should == {:controller => '<%= model_controller_controller_name %>', :action => 'new', :format => 'xml'}
+ params_from(:get, '/<%= model_controller_routing_path %>/new.json').should == {:controller => '<%= model_controller_controller_name %>', :action => 'new', :format => 'json'}
+ end
+
+ it "should generate params for <%= model_controller_controller_name %>'s create action from POST /<%= model_controller_routing_path %>" do
+ params_from(:post, '/<%= model_controller_routing_path %>').should == {:controller => '<%= model_controller_controller_name %>', :action => 'create'}
+ params_from(:post, '/<%= model_controller_routing_path %>.xml').should == {:controller => '<%= model_controller_controller_name %>', :action => 'create', :format => 'xml'}
+ params_from(:post, '/<%= model_controller_routing_path %>.json').should == {:controller => '<%= model_controller_controller_name %>', :action => 'create', :format => 'json'}
+ end
+
+ it "should generate params for <%= model_controller_controller_name %>'s show action from GET /<%= model_controller_routing_path %>/1" do
+ params_from(:get , '/<%= model_controller_routing_path %>/1').should == {:controller => '<%= model_controller_controller_name %>', :action => 'show', :id => '1'}
+ params_from(:get , '/<%= model_controller_routing_path %>/1.xml').should == {:controller => '<%= model_controller_controller_name %>', :action => 'show', :id => '1', :format => 'xml'}
+ params_from(:get , '/<%= model_controller_routing_path %>/1.json').should == {:controller => '<%= model_controller_controller_name %>', :action => 'show', :id => '1', :format => 'json'}
+ end
+
+ it "should generate params for <%= model_controller_controller_name %>'s edit action from GET /<%= model_controller_routing_path %>/1/edit" do
+ params_from(:get , '/<%= model_controller_routing_path %>/1/edit').should == {:controller => '<%= model_controller_controller_name %>', :action => 'edit', :id => '1'}
+ end
+
+ it "should generate params {:controller => '<%= model_controller_controller_name %>', :action => update', :id => '1'} from PUT /<%= model_controller_routing_path %>/1" do
+ params_from(:put , '/<%= model_controller_routing_path %>/1').should == {:controller => '<%= model_controller_controller_name %>', :action => 'update', :id => '1'}
+ params_from(:put , '/<%= model_controller_routing_path %>/1.xml').should == {:controller => '<%= model_controller_controller_name %>', :action => 'update', :id => '1', :format => 'xml'}
+ params_from(:put , '/<%= model_controller_routing_path %>/1.json').should == {:controller => '<%= model_controller_controller_name %>', :action => 'update', :id => '1', :format => 'json'}
+ end
+
+ it "should generate params for <%= model_controller_controller_name %>'s destroy action from DELETE /<%= model_controller_routing_path %>/1" do
+ params_from(:delete, '/<%= model_controller_routing_path %>/1').should == {:controller => '<%= model_controller_controller_name %>', :action => 'destroy', :id => '1'}
+ params_from(:delete, '/<%= model_controller_routing_path %>/1.xml').should == {:controller => '<%= model_controller_controller_name %>', :action => 'destroy', :id => '1', :format => 'xml'}
+ params_from(:delete, '/<%= model_controller_routing_path %>/1.json').should == {:controller => '<%= model_controller_controller_name %>', :action => 'destroy', :id => '1', :format => 'json'}
+ end
+ end
+
+ describe "named routing" do
+ before(:each) do
+ get :new
+ end
+
+ it "should route <%= model_controller_routing_name %>_path() to /<%= model_controller_routing_path %>" do
+ <%= model_controller_routing_name %>_path().should == "/<%= model_controller_routing_path %>"
+ formatted_<%= model_controller_routing_name %>_path(:format => 'xml').should == "/<%= model_controller_routing_path %>.xml"
+ formatted_<%= model_controller_routing_name %>_path(:format => 'json').should == "/<%= model_controller_routing_path %>.json"
+ end
+
+ it "should route new_<%= model_controller_routing_name.singularize %>_path() to /<%= model_controller_routing_path %>/new" do
+ new_<%= model_controller_routing_name.singularize %>_path().should == "/<%= model_controller_routing_path %>/new"
+ formatted_new_<%= model_controller_routing_name.singularize %>_path(:format => 'xml').should == "/<%= model_controller_routing_path %>/new.xml"
+ formatted_new_<%= model_controller_routing_name.singularize %>_path(:format => 'json').should == "/<%= model_controller_routing_path %>/new.json"
+ end
+
+ it "should route <%= model_controller_routing_name.singularize %>_(:id => '1') to /<%= model_controller_routing_path %>/1" do
+ <%= model_controller_routing_name.singularize %>_path(:id => '1').should == "/<%= model_controller_routing_path %>/1"
+ formatted_<%= model_controller_routing_name.singularize %>_path(:id => '1', :format => 'xml').should == "/<%= model_controller_routing_path %>/1.xml"
+ formatted_<%= model_controller_routing_name.singularize %>_path(:id => '1', :format => 'json').should == "/<%= model_controller_routing_path %>/1.json"
+ end
+
+ it "should route edit_<%= model_controller_routing_name.singularize %>_path(:id => '1') to /<%= model_controller_routing_path %>/1/edit" do
+ edit_<%= model_controller_routing_name.singularize %>_path(:id => '1').should == "/<%= model_controller_routing_path %>/1/edit"
+ end
+ end
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/fixtures/users.yml
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/fixtures/users.yml (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/fixtures/users.yml 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,60 @@
+<%
+ ## this code must match that in templates/model.rb
+ require 'digest/sha1'
+ def make_fake_token
+ @fake_token_counter ||= 0
+ @fake_token_counter += 1
+ Digest::SHA1.hexdigest(@fake_token_counter.to_s)
+ end
+ salts = (1..2).map{ make_fake_token }
+ passwds = salts.map{ |salt| password_digest('monkey', salt) }
+-%>
+
+quentin:
+ id: 1
+ login: quentin
+ email: quentin at example.com
+ salt: <%= salts[0] %> # SHA1('0')
+ crypted_password: <%= passwds[0] %> # 'monkey'
+ created_at: <%%= 5.days.ago.to_s :db %>
+ remember_token_expires_at: <%%= 1.days.from_now.to_s %>
+ remember_token: <%= make_fake_token %>
+<% if options[:include_activation] -%>
+ activation_code:
+ activated_at: <%%= 5.days.ago.to_s :db %>
+<% end -%>
+<% if options[:stateful] -%>
+ state: active
+<% end -%>
+
+aaron:
+ id: 2
+ login: aaron
+ email: aaron at example.com
+ salt: <%= salts[1] %> # SHA1('1')
+ crypted_password: <%= passwds[1] %> # 'monkey'
+ created_at: <%%= 1.days.ago.to_s :db %>
+ remember_token_expires_at:
+ remember_token:
+<% if options[:include_activation] -%>
+ activation_code: <%= make_fake_token %>
+ activated_at:
+<% end -%>
+<% if options[:stateful] %>
+ state: pending
+<% end -%>
+
+
+old_password_holder:
+ id: 3
+ login: old_password_holder
+ email: salty_dog at example.com
+ salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
+ crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
+ created_at: <%%= 1.days.ago.to_s :db %>
+<% if options[:include_activation] %>
+ activation_code:
+ activated_at: <%%= 5.days.ago.to_s :db %>
+<% end %>
+<% if options[:stateful] %>
+ state: active<% end %>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/helpers/users_helper_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/helpers/users_helper_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/helpers/users_helper_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,141 @@
+require File.dirname(__FILE__) + '<%= ('/..'*model_controller_class_nesting_depth) + '/../spec_helper' %>'
+include ApplicationHelper
+include <%= model_controller_class_name %>Helper
+include AuthenticatedTestHelper
+
+describe <%= model_controller_class_name %>Helper do
+ before do
+ @<%= file_name %> = mock_<%= file_name %>
+ end
+
+ describe "if_authorized" do
+ it "yields if authorized" do
+ should_receive(:authorized?).with('a','r').and_return(true)
+ if_authorized?('a','r'){|action,resource| [action,resource,'hi'] }.should == ['a','r','hi']
+ end
+ it "does nothing if not authorized" do
+ should_receive(:authorized?).with('a','r').and_return(false)
+ if_authorized?('a','r'){ 'hi' }.should be_nil
+ end
+ end
+
+ describe "link_to_<%= file_name %>" do
+ it "should give an error on a nil <%= file_name %>" do
+ lambda { link_to_<%= file_name %>(nil) }.should raise_error('Invalid <%= file_name %>')
+ end
+ it "should link to the given <%= file_name %>" do
+ should_receive(:<%= model_controller_routing_name.singularize %>_path).at_least(:once).and_return('/<%= model_controller_file_path %>/1')
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a[href='/<%= model_controller_file_path %>/1']")
+ end
+ it "should use given link text if :content_text is specified" do
+ link_to_<%= file_name %>(@<%= file_name %>, :content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+ it "should use the login as link text with no :content_method specified" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a", 'user_name')
+ end
+ it "should use the name as link text with :content_method => :name" do
+ link_to_<%= file_name %>(@<%= file_name %>, :content_method => :name).should have_tag("a", 'U. Surname')
+ end
+ it "should use the login as title with no :title_method specified" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a[title='user_name']")
+ end
+ it "should use the name as link title with :content_method => :name" do
+ link_to_<%= file_name %>(@<%= file_name %>, :title_method => :name).should have_tag("a[title='U. Surname']")
+ end
+ it "should have nickname as a class by default" do
+ link_to_<%= file_name %>(@<%= file_name %>).should have_tag("a.nickname")
+ end
+ it "should take other classes and no longer have the nickname class" do
+ result = link_to_<%= file_name %>(@<%= file_name %>, :class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+ end
+
+ describe "link_to_login_with_IP" do
+ it "should link to the login_path" do
+ link_to_login_with_IP().should have_tag("a[href='/login']")
+ end
+ it "should use given link text if :content_text is specified" do
+ link_to_login_with_IP('Hello there!').should have_tag("a", 'Hello there!')
+ end
+ it "should use the login as link text with no :content_method specified" do
+ link_to_login_with_IP().should have_tag("a", '0.0.0.0')
+ end
+ it "should use the ip address as title" do
+ link_to_login_with_IP().should have_tag("a[title='0.0.0.0']")
+ end
+ it "should by default be like school in summer and have no class" do
+ link_to_login_with_IP().should_not have_tag("a.nickname")
+ end
+ it "should have some class if you tell it to" do
+ result = link_to_login_with_IP(nil, :class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+ it "should have some class if you tell it to" do
+ result = link_to_login_with_IP(nil, :tag => 'abbr')
+ result.should have_tag("abbr[title='0.0.0.0']")
+ end
+ end
+
+ describe "link_to_current_<%= file_name %>, When logged in" do
+ before do
+ stub!(:current_<%= file_name %>).and_return(@<%= file_name %>)
+ end
+ it "should link to the given <%= file_name %>" do
+ should_receive(:<%= model_controller_routing_name.singularize %>_path).at_least(:once).and_return('/<%= model_controller_file_path %>/1')
+ link_to_current_<%= file_name %>().should have_tag("a[href='/<%= model_controller_file_path %>/1']")
+ end
+ it "should use given link text if :content_text is specified" do
+ link_to_current_<%= file_name %>(:content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+ it "should use the login as link text with no :content_method specified" do
+ link_to_current_<%= file_name %>().should have_tag("a", 'user_name')
+ end
+ it "should use the name as link text with :content_method => :name" do
+ link_to_current_<%= file_name %>(:content_method => :name).should have_tag("a", 'U. Surname')
+ end
+ it "should use the login as title with no :title_method specified" do
+ link_to_current_<%= file_name %>().should have_tag("a[title='user_name']")
+ end
+ it "should use the name as link title with :content_method => :name" do
+ link_to_current_<%= file_name %>(:title_method => :name).should have_tag("a[title='U. Surname']")
+ end
+ it "should have nickname as a class" do
+ link_to_current_<%= file_name %>().should have_tag("a.nickname")
+ end
+ it "should take other classes and no longer have the nickname class" do
+ result = link_to_current_<%= file_name %>(:class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+ end
+
+ describe "link_to_current_<%= file_name %>, When logged out" do
+ before do
+ stub!(:current_<%= file_name %>).and_return(nil)
+ end
+ it "should link to the login_path" do
+ link_to_current_<%= file_name %>().should have_tag("a[href='/login']")
+ end
+ it "should use given link text if :content_text is specified" do
+ link_to_current_<%= file_name %>(:content_text => 'Hello there!').should have_tag("a", 'Hello there!')
+ end
+ it "should use 'not signed in' as link text with no :content_method specified" do
+ link_to_current_<%= file_name %>().should have_tag("a", 'not signed in')
+ end
+ it "should use the ip address as title" do
+ link_to_current_<%= file_name %>().should have_tag("a[title='0.0.0.0']")
+ end
+ it "should by default be like school in summer and have no class" do
+ link_to_current_<%= file_name %>().should_not have_tag("a.nickname")
+ end
+ it "should have some class if you tell it to" do
+ result = link_to_current_<%= file_name %>(:class => 'foo bar')
+ result.should have_tag("a.foo")
+ result.should have_tag("a.bar")
+ end
+ end
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/models/user_spec.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/models/user_spec.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/spec/models/user_spec.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,290 @@
+# -*- coding: mule-utf-8 -*-
+require File.dirname(__FILE__) + '<%= ('/..'*model_controller_class_nesting_depth) + '/../spec_helper' %>'
+
+# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead.
+# Then, you can remove it from this and the functional test.
+include AuthenticatedTestHelper
+
+describe <%= class_name %> do
+ fixtures :<%= table_name %>
+
+ describe 'being created' do
+ before do
+ @<%= file_name %> = nil
+ @creating_<%= file_name %> = lambda do
+ @<%= file_name %> = create_<%= file_name %>
+ violated "#{@<%= file_name %>.errors.full_messages.to_sentence}" if @<%= file_name %>.new_record?
+ end
+ end
+
+ it 'increments <%= class_name %>#count' do
+ @creating_<%= file_name %>.should change(<%= class_name %>, :count).by(1)
+ end
+<% if options[:include_activation] %>
+ it 'initializes #activation_code' do
+ @creating_<%= file_name %>.call
+ @<%= file_name %>.reload
+ @<%= file_name %>.activation_code.should_not be_nil
+ end
+<% end %><% if options[:stateful] %>
+ it 'starts in pending state' do
+ @creating_<%= file_name %>.call
+ @<%= file_name %>.reload
+ @<%= file_name %>.should be_pending
+ end
+<% end %> end
+
+ #
+ # Validations
+ #
+
+ it 'requires login' do
+ lambda do
+ u = create_<%= file_name %>(:login => nil)
+ u.errors.on(:login).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ describe 'allows legitimate logins:' do
+ ['123', '1234567890_234567890_234567890_234567890',
+ 'hello.-_there at funnychar.com'].each do |login_str|
+ it "'#{login_str}'" do
+ lambda do
+ u = create_<%= file_name %>(:login => login_str)
+ u.errors.on(:login).should be_nil
+ end.should change(<%= class_name %>, :count).by(1)
+ end
+ end
+ end
+ describe 'disallows illegitimate logins:' do
+ ['12', '1234567890_234567890_234567890_234567890_', "tab\t", "newline\n",
+ "Iñtërnâtiônàlizætiøn hasn't happened to ruby 1.8 yet",
+ 'semicolon;', 'quote"', 'tick\'', 'backtick`', 'percent%', 'plus+', 'space '].each do |login_str|
+ it "'#{login_str}'" do
+ lambda do
+ u = create_<%= file_name %>(:login => login_str)
+ u.errors.on(:login).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+ end
+ end
+
+ it 'requires password' do
+ lambda do
+ u = create_<%= file_name %>(:password => nil)
+ u.errors.on(:password).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ it 'requires password confirmation' do
+ lambda do
+ u = create_<%= file_name %>(:password_confirmation => nil)
+ u.errors.on(:password_confirmation).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ it 'requires email' do
+ lambda do
+ u = create_<%= file_name %>(:email => nil)
+ u.errors.on(:email).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+
+ describe 'allows legitimate emails:' do
+ ['foo at bar.com', 'foo at newskool-tld.museum', 'foo at twoletter-tld.de', 'foo at nonexistant-tld.qq',
+ 'r at a.wk', '1234567890-234567890-234567890-234567890-234567890-234567890-234567890-234567890-234567890 at gmail.com',
+ 'hello.-_there at funnychar.com', 'uucp%addr at gmail.com', 'hello+routing-str at gmail.com',
+ 'domain at can.haz.many.sub.doma.in',
+ ].each do |email_str|
+ it "'#{email_str}'" do
+ lambda do
+ u = create_<%= file_name %>(:email => email_str)
+ u.errors.on(:email).should be_nil
+ end.should change(<%= class_name %>, :count).by(1)
+ end
+ end
+ end
+ describe 'disallows illegitimate emails' do
+ ['!!@nobadchars.com', 'foo at no-rep-dots..com', 'foo at badtld.xxx', 'foo at toolongtld.abcdefg',
+ 'Iñtërnâtiônàlizætiøn at hasnt.happened.to.email', 'need.domain.and.tld at de', "tab\t", "newline\n",
+ 'r at .wk', '1234567890-234567890-234567890-234567890-234567890-234567890-234567890-234567890-234567890 at gmail2.com',
+ # these are technically allowed but not seen in practice:
+ 'uucp!addr at gmail.com', 'semicolon;@gmail.com', 'quote"@gmail.com', 'tick\'@gmail.com', 'backtick`@gmail.com', 'space @gmail.com', 'bracket<@gmail.com', 'bracket>@gmail.com'
+ ].each do |email_str|
+ it "'#{email_str}'" do
+ lambda do
+ u = create_<%= file_name %>(:email => email_str)
+ u.errors.on(:email).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+ end
+ end
+
+ describe 'allows legitimate names:' do
+ ['Andre The Giant (7\'4", 520 lb.) -- has a posse',
+ '', '1234567890_234567890_234567890_234567890_234567890_234567890_234567890_234567890_234567890_234567890',
+ ].each do |name_str|
+ it "'#{name_str}'" do
+ lambda do
+ u = create_<%= file_name %>(:name => name_str)
+ u.errors.on(:name).should be_nil
+ end.should change(<%= class_name %>, :count).by(1)
+ end
+ end
+ end
+ describe "disallows illegitimate names" do
+ ["tab\t", "newline\n",
+ '1234567890_234567890_234567890_234567890_234567890_234567890_234567890_234567890_234567890_234567890_',
+ ].each do |name_str|
+ it "'#{name_str}'" do
+ lambda do
+ u = create_<%= file_name %>(:name => name_str)
+ u.errors.on(:name).should_not be_nil
+ end.should_not change(<%= class_name %>, :count)
+ end
+ end
+ end
+
+ it 'resets password' do
+ <%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
+ <%= class_name %>.authenticate('quentin', 'new password').should == <%= table_name %>(:quentin)
+ end
+
+ it 'does not rehash password' do
+ <%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
+ <%= class_name %>.authenticate('quentin2', 'monkey').should == <%= table_name %>(:quentin)
+ end
+
+ #
+ # Authentication
+ #
+
+ it 'authenticates <%= file_name %>' do
+ <%= class_name %>.authenticate('quentin', 'monkey').should == <%= table_name %>(:quentin)
+ end
+
+ it "doesn't authenticates <%= file_name %> with bad password" do
+ <%= class_name %>.authenticate('quentin', 'monkey').should == <%= table_name %>(:quentin)
+ end
+
+ if REST_AUTH_SITE_KEY.blank?
+ # old-school passwords
+ it "authenticates a user against a hard-coded old-style password" do
+ <%= class_name %>.authenticate('old_password_holder', 'test').should == <%= table_name %>(:old_password_holder)
+ end
+ else
+ it "doesn't authenticate a user against a hard-coded old-style password" do
+ <%= class_name %>.authenticate('old_password_holder', 'test').should be_nil
+ end
+
+ # New installs should bump this up and set REST_AUTH_DIGEST_STRETCHES to give a 10ms encrypt time or so
+ desired_encryption_expensiveness_ms = 0.1
+ it "takes longer than #{desired_encryption_expensiveness_ms}ms to encrypt a password" do
+ test_reps = 100
+ start_time = Time.now; test_reps.times{ <%= class_name %>.authenticate('quentin', 'monkey'+rand.to_s) }; end_time = Time.now
+ auth_time_ms = 1000 * (end_time - start_time)/test_reps
+ auth_time_ms.should > desired_encryption_expensiveness_ms
+ end
+ end
+
+ #
+ # Authentication
+ #
+
+ it 'sets remember token' do
+ <%= table_name %>(:quentin).remember_me
+ <%= table_name %>(:quentin).remember_token.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
+ end
+
+ it 'unsets remember token' do
+ <%= table_name %>(:quentin).remember_me
+ <%= table_name %>(:quentin).remember_token.should_not be_nil
+ <%= table_name %>(:quentin).forget_me
+ <%= table_name %>(:quentin).remember_token.should be_nil
+ end
+
+ it 'remembers me for one week' do
+ before = 1.week.from_now.utc
+ <%= table_name %>(:quentin).remember_me_for 1.week
+ after = 1.week.from_now.utc
+ <%= table_name %>(:quentin).remember_token.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after).should be_true
+ end
+
+ it 'remembers me until one week' do
+ time = 1.week.from_now.utc
+ <%= table_name %>(:quentin).remember_me_until time
+ <%= table_name %>(:quentin).remember_token.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.should == time
+ end
+
+ it 'remembers me default two weeks' do
+ before = 2.weeks.from_now.utc
+ <%= table_name %>(:quentin).remember_me
+ after = 2.weeks.from_now.utc
+ <%= table_name %>(:quentin).remember_token.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
+ <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after).should be_true
+ end
+<% if options[:stateful] %>
+ it 'registers passive <%= file_name %>' do
+ <%= file_name %> = create_<%= file_name %>(:password => nil, :password_confirmation => nil)
+ <%= file_name %>.should be_passive
+ <%= file_name %>.update_attributes(:password => 'new password', :password_confirmation => 'new password')
+ <%= file_name %>.register!
+ <%= file_name %>.should be_pending
+ end
+
+ it 'suspends <%= file_name %>' do
+ <%= table_name %>(:quentin).suspend!
+ <%= table_name %>(:quentin).should be_suspended
+ end
+
+ it 'does not authenticate suspended <%= file_name %>' do
+ <%= table_name %>(:quentin).suspend!
+ <%= class_name %>.authenticate('quentin', 'monkey').should_not == <%= table_name %>(:quentin)
+ end
+
+ it 'deletes <%= file_name %>' do
+ <%= table_name %>(:quentin).deleted_at.should be_nil
+ <%= table_name %>(:quentin).delete!
+ <%= table_name %>(:quentin).deleted_at.should_not be_nil
+ <%= table_name %>(:quentin).should be_deleted
+ end
+
+ describe "being unsuspended" do
+ fixtures :<%= table_name %>
+
+ before do
+ @<%= file_name %> = <%= table_name %>(:quentin)
+ @<%= file_name %>.suspend!
+ end
+
+ it 'reverts to active state' do
+ @<%= file_name %>.unsuspend!
+ @<%= file_name %>.should be_active
+ end
+
+ it 'reverts to passive state if activation_code and activated_at are nil' do
+ <%= class_name %>.update_all :activation_code => nil, :activated_at => nil
+ @<%= file_name %>.reload.unsuspend!
+ @<%= file_name %>.should be_passive
+ end
+
+ it 'reverts to pending state if activation_code is set and activated_at is nil' do
+ <%= class_name %>.update_all :activation_code => 'foo-bar', :activated_at => nil
+ @<%= file_name %>.reload.unsuspend!
+ @<%= file_name %>.should be_pending
+ end
+ end
+<% end %>
+protected
+ def create_<%= file_name %>(options = {})
+ record = <%= class_name %>.new({ :login => 'quire', :email => 'quire at example.com', :password => 'quire69', :password_confirmation => 'quire69' }.merge(options))
+ record.<% if options[:stateful] %>register! if record.valid?<% else %>save<% end %>
+ record
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,22 @@
+#!/usr/bin/env ruby
+ENV["RAILS_ENV"] = "test"
+require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
+require 'spec/rails/story_adapter'
+require 'spec/story'
+require File.expand_path(File.dirname(__FILE__) + "/rest_auth_stories_helper.rb")
+
+# Make visible for testing
+ApplicationController.send(:public, :logged_in?, :current_user, :authorized?)
+
+this_dir = File.dirname(__FILE__)
+Dir[File.join(this_dir, "steps/*.rb")].each do |file|
+ puts file.to_s
+ require file
+end
+
+with_steps_for :ra_navigation, :ra_response, :ra_resource, :<%= file_name %> do
+ story_files = Dir[File.join(this_dir, "<%= table_name %>", '*.story')]
+ story_files.each do |file|
+ run file, :type => RailsStory
+ end
+end
Property changes on: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories.rb
___________________________________________________________________
Name: svn:executable
+ *
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories_helper.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories_helper.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/rest_auth_stories_helper.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,81 @@
+# If you have a global stories helper, move this line there:
+include AuthenticatedTestHelper
+
+# Most of the below came out of code from Ben Mabey
+# http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/
+
+# These allow exceptions to come through as opposed to being caught and hvaing non-helpful responses returned.
+ActionController::Base.class_eval do
+ def perform_action
+ perform_action_without_rescue
+ end
+end
+Dispatcher.class_eval do
+ def self.failsafe_response(output, status, exception = nil)
+ raise exception
+ end
+end
+
+#
+# Sugar for turning a story's attribute list into list, array, etc.
+#
+module ToFooFromStory
+ def ToFooFromStory.fix_key key
+ key.downcase.gsub(/\s+/, '_')
+ end
+ def ToFooFromStory.fix_value value
+ return '' if !value
+ value.strip!
+ case
+ when value =~ /^'(.*)'$/ then value = $1
+ when value =~ /^"(.*)"$/ then value = $1
+ when value == 'nil!' then value = nil
+ when value == 'non-nil!' then value = be_nil
+ when value =~ /^#\{(.*)\}$/ then value = eval($1)
+ end
+ value
+ end
+ # Converts a key: value list found in the steps into a hash.
+ # Example:
+ # ISBN: '0967539854' and comment: 'I love this book' and Quality rating: '4'
+ # # => {"quality_rating"=>"4", "isbn"=>"0967539854", "comment"=>"I love this book"}
+ def to_hash_from_story
+ hsh = self.split(/,? and |, /).inject({}) do |hash_so_far, key_value|
+ key, value = key_value.split(":")
+ if !value then warn "Couldn't understand story '#{self}': only understood up to the part '#{hash_so_far.to_yaml}'" end
+ hash_so_far.merge(ToFooFromStory::fix_key(key) => ToFooFromStory::fix_value(value))
+ end
+ end
+ # Coverts an attribute list found in the steps into an array
+ # Example:
+ # login, email, updated_at, and gravatar
+ # # => ['login', 'email', 'updated_at', 'gravatar']
+ def to_array_from_story
+ self.split(/,? and |, /).map do |value|
+ ToFooFromStory::fix_value(value)
+ end
+ end
+end
+class String
+ include ToFooFromStory
+end
+
+def instantize(string)
+ instance_variable_get("@#{string}")
+end
+
+#
+# Spew response onto screen -- painful but scrolling >> debugger
+#
+def dump_response
+ # note that @request and @template won't to_yaml and that @session includes @cgi
+ response_methods = response.instance_variables - ['@request', '@template', '@cgi']
+ request_methods = response.request.instance_variables - ['@session_options_with_string_keys', '@cgi', '@session']
+ response_methods.map!{|attr| attr.gsub(/^@/,'')}.sort!
+ request_methods.map!{ |attr| attr.gsub(/^@/,'')}.sort!
+ puts '', '*' * 75,
+ response.instance_values.slice(*response_methods).to_yaml,
+ "*" * 75, '',
+ response.request.instance_values.slice(*request_methods).to_yaml,
+ "*" * 75, ''
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_navigation_steps.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_navigation_steps.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_navigation_steps.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,49 @@
+#
+# Where to go
+#
+steps_for(:ra_navigation) do
+ #
+ # GET
+ # Go to a given page.
+ When "$actor goes to $path" do |actor, path|
+ case path
+ when 'the home page' then get '/'
+ else get path
+ end
+ end
+
+ # POST -- Ex:
+ # When she creates a book with ISBN: '0967539854' and comment: 'I love this book' and rating: '4'
+ # When she creates a singular session with login: 'reggie' and password: 'i_haxxor_joo'
+ # Since I'm not smrt enough to do it right, explicitly specify singular resources
+ When %r{$actor creates an? $resource with $attributes} do |actor, resource, attributes|
+ attributes = attributes.to_hash_from_story
+ if resource =~ /singular (\w+)/
+ resource = $1.downcase.singularize
+ post "/#{resource}", attributes
+ else
+ post "/#{resource.downcase.pluralize}", { resource.downcase.singularize => attributes }
+ end
+ end
+
+ # PUT
+ When %r{$actor asks to update '$resource' with $attributes} do |_, resource, attributes|
+ attributes = attributes.to_hash_from_story
+ put "#{resource}", attributes
+ dump_response
+ end
+
+ # DELETE -- Slap together the POST-form-as-fake-HTTP-DELETE submission
+ When %r{$actor asks to delete '$resource'} do |_, resource|
+ post "/#{resource.downcase.pluralize}", { :_method => :delete }
+ dump_response
+ end
+
+
+ # Redirect --
+ # Rather than coding in get/get_via_redirect's and past/p_v_r's,
+ # let's just demand that in the story itself.
+ When "$actor follows that redirect!" do |actor|
+ follow_redirect!
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_resource_steps.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_resource_steps.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_resource_steps.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,179 @@
+# The flexible code for resource testing came out of code from Ben Mabey
+# http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/
+steps_for(:ra_resource) do
+ #
+ # Construct resources
+ #
+
+ #
+ # Build a resource as described, store it as an @instance variable. Ex:
+ # "Given a <%= file_name %> with login: 'mojojojo'"
+ # produces a <%= class_name %> instance stored in @<%= file_name %> with 'mojojojo' as its login
+ # attribute.
+ #
+ Given "a $resource instance with $attributes" do |resource, attributes|
+ klass, instance, attributes = parse_resource_args resource, attributes
+ instance = klass.new(attributes)
+ instance.save!
+ find_resource(resource, attributes).should_not be_nil
+ keep_instance! resource, instance
+ end
+
+ #
+ # Stuff attributes into a preexisting @resource
+ # "And the <%= file_name %> has thac0: 3"
+ # takes the earlier-defined @<%= file_name %> instance and sets its thac0 to '3'.
+ #
+ Given "the $resource has $attributes" do |resource, attributes|
+ klass, instance, attributes = parse_resource_args resource, attributes
+ attributes.each do |attr, val|
+ instance.send("#{attr}=", val)
+ end
+ instance.save!
+ find_resource(resource, attributes).should_not be_nil
+ keep_instance! resource, instance
+ end
+
+ #
+ # Destroy all for this resource
+ #
+ Given "no $resource with $attr: '$val' exists" do |resource, attr, val|
+ klass, instance = parse_resource_args resource
+ klass.destroy_all(attr.to_sym => val)
+ instance = find_resource resource, attr.to_sym => val
+ instance.should be_nil
+ keep_instance! resource, instance
+ end
+
+ #
+ # Then's for resources
+ #
+
+ # Resource like this DOES exist
+ Then %r{an? $resource with $attributes should exist} do |resource, attributes|
+ instance = find_resource resource, attributes
+ instance.should_not be_nil
+ keep_instance! resource, instance
+ end
+ # Resource like this DOES NOT exist
+ Then %r{no $resource with $attributes should exist} do |resource, attributes|
+ instance = find_resource resource, attributes
+ instance.should be_nil
+ end
+
+ # Resource has attributes with given values
+ Then "the $resource should have $attributes" do |resource, attributes|
+ klass, instance, attributes = parse_resource_args resource, attributes
+ attributes.each do |attr, val|
+ instance.send(attr).should == val
+ end
+ end
+ # Resource attributes should / should not be nil
+ Then "the $resource's $attr should be nil" do |resource, attr|
+ klass, instance = parse_resource_args resource
+ instance.send(attr).should be_nil
+ end
+ Then "the $resource's $attr should not be nil" do |resource, attr|
+ klass, instance = parse_resource_args resource
+ instance.send(attr).should_not be_nil
+ end
+
+ #
+ # Bank each of the @resource's listed attributes for later.
+ #
+ Given "we try hard to remember the $resource's $attributes" do |resource, attributes|
+ attributes = attributes.to_array_from_story
+ attributes.each do |attr|
+ memorize_resource_value resource, attr
+ end
+ end
+ #
+ # Bank each of the @resource's listed attributes for later.
+ #
+ Given "we don't remember anything about the past" do
+ memorize_forget_all!
+ end
+
+ #
+ # Compare @resource.attr to its earlier-memorized value.
+ # Specify ' using method_name' (abs, to_s, &c) to coerce before comparing.
+ # For important and mysterious reasons, timestamps want to_i or to_s.
+ #
+ Then %r{the $resource\'s $attribute should stay the same(?: under $func)?} do |resource, attr, func|
+ klass, instance = parse_resource_args resource
+ # Get the values
+ old_value = recall_resource_value(resource, attr)
+ new_value = instance.send(attr)
+ # Transform each value, maybe, using value.func
+ if func then new_value = new_value.send(func); old_value = old_value.send(func) end
+ # Compare
+ old_value.should eql(new_value)
+ end
+
+ #
+ # Look for each for the given attributes in the page's text
+ #
+ Then "page should have the $resource's $attributes" do |resource, attributes|
+ actual_resource = instantize(resource)
+ attributes.split(/, and |, /).each do |attribute|
+ response.should have_text(/#{actual_resource.send(attribute.strip.gsub(" ","_"))}/)
+ end
+ end
+
+end
+
+#
+# Turn a resource name and a to_hash_from_story string like
+# "attr: 'value', attr2: 'value2', ... , and attrN: 'valueN'"
+# into
+# * klass -- the class matching that Resource
+# * instance -- the possibly-preexisting local instance value @resource
+# * attributes -- a hash matching the given attribute-list string
+#
+def parse_resource_args resource, attributes=nil
+ instance = instantize resource
+ klass = resource.classify.constantize
+ attributes = attributes.to_hash_from_story if attributes
+ [klass, instance, attributes]
+end
+
+#
+# Given a class name 'resource' and a hash of conditsion, find a model
+#
+def find_resource resource, conditions
+ klass, instance = parse_resource_args resource
+ conditions = conditions.to_hash_from_story unless (conditions.is_a? Hash)
+ klass.find(:first, :conditions => conditions)
+end
+
+#
+# Simple, brittle, useful: store the given resource's attribute
+# so we can compare it later.
+#
+def memorize_resource_value resource, attr
+ klass, instance = parse_resource_args resource
+ value = instance.send(attr)
+ @_memorized ||= {}
+ @_memorized[resource] ||= {}
+ @_memorized[resource][attr] = value
+ value
+end
+def recall_resource_value resource, attr
+ @_memorized[resource][attr]
+end
+def memorize_forget_all!
+ @_memorized = {}
+end
+
+#
+# Keep the object around in a local instance variable @resource.
+#
+# So, for instance,
+# klass, instance = parse_resource_args '<%= file_name %>'
+# instance = klass.new({login => 'me', password => 'monkey', ...})
+# keep_instance! resource, instance
+# keeps the just-constructed <%= class_name %> model in the @<%= file_name %> instance variable.
+#
+def keep_instance! resource, object
+ instance_variable_set("@#{resource}", object)
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_response_steps.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_response_steps.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/ra_response_steps.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,171 @@
+#
+# What you should see when you get there
+#
+
+steps_for(:ra_response) do
+ #
+ # Destinations. Ex:
+ # She should be at the new kids page
+ # Tarkin should be at the destroy alderaan page
+ # The visitor should be at the '/lolcats/download' form
+ # The visitor should be redirected to '/hi/mom'
+ #
+ # It doesn't know anything about actual routes -- it just
+ # feeds its output to render_template or redirect_to
+ #
+ Then "$actor should be at $path" do |_, path|
+ response.should render_template(grok_path(path))
+ end
+
+ Then "$actor should be redirected to $path" do |_, path|
+ response.should redirect_to(grok_path(path))
+ end
+
+ Then "the page should look AWESOME" do
+ response.should have_tag('head>title')
+ response.should have_tag('h1')
+ # response.should be_valid_xhtml
+ end
+
+ #
+ # Tags
+ #
+
+ Then "the page should contain '$text'" do |_, text|
+ response.should have_text(/#{text}/)
+ end
+
+ # please note: this enforces the use of a <label> field
+ Then "$actor should see a <$container> containing a $attributes" do |_, container, attributes|
+ attributes = attributes.to_hash_from_story
+ response.should have_tag(container) do
+ attributes.each do |tag, label|
+ case tag
+ when "textfield" then with_tag "input[type='text']"; with_tag("label", label)
+ when "password" then with_tag "input[type='password']"; with_tag("label", label)
+ when "submit" then with_tag "input[type='submit'][value='#{label}']"
+ else with_tag tag, label
+ end
+ end
+ end
+ end
+
+ #
+ # Session, cookie variables
+ #
+ Then "$actor $token cookie should include $attrlist" do |_, token, attrlist|
+ attrlist = attrlist.to_array_from_story
+ cookies.include?(token).should be_true
+ attrlist.each do |val|
+ cookies[token].include?(val).should be_true
+ end
+ end
+
+ Then "$actor $token cookie should exist but not include $attrlist" do |_, token, attrlist|
+ attrlist = attrlist.to_array_from_story
+ cookies.include?(token).should be_true
+ puts [cookies, attrlist, token].to_yaml
+ attrlist.each do |val|
+ cookies[token].include?(val).should_not be_true
+ end
+ end
+
+ Then "$actor should have $an $token cookie" do |_, _, token|
+ cookies[token].should_not be_blank
+ end
+ Then "$actor should not have $an $token cookie" do |_, _, token|
+ cookies[token].should be_blank
+ end
+
+ Given "$actor has $an cookie jar with $attributes" do |_, _, attributes|
+ attributes = attributes.to_hash_from_story
+ attributes.each do |attr, val|
+ cookies[attr] = val
+ end
+ end
+ Given "$actor session store has no $attrlist" do |_, attrlist|
+ attrlist = attrlist.to_array_from_story
+ attrlist.each do |attr|
+ # Note that the comparison passes through 'to_s'
+ session[attr.to_sym] = nil
+ end
+ end
+
+ Then "$actor session store should have $attributes" do |_, attributes|
+ attributes = attributes.to_hash_from_story
+ attributes.each do |attr, val|
+ # Note that the comparison passes through 'to_s'
+ session[attr.to_sym].to_s.should eql(val)
+ end
+ end
+
+ Then "$actor session store should not have $attrlist" do |_, attrlist|
+ attrlist = attrlist.to_array_from_story
+ attrlist.each do |attr|
+ session[attr.to_sym].blank?.should be_true
+ end
+ end
+
+ #
+ # Flash messages
+ #
+
+ Then "$actor should see $an $notice message '$message'" do |_, _, notice, message|
+ response.should have_flash(notice, %r{#{message}})
+ end
+
+ Then "$actor should not see $an $notice message '$message'" do |_, _, notice, message|
+ response.should_not have_flash(notice, %r{#{message}})
+ end
+
+ Then "$actor should see no messages" do |_|
+ ['error', 'warning', 'notice'].each do |notice|
+ response.should_not have_flash(notice)
+ end
+ end
+
+ RE_POLITENESS = /(?:please|sorry|thank(?:s| you))/i
+ Then %r{we should be polite about it} do
+ response.should have_tag("div.error,div.notice", RE_POLITENESS)
+ end
+ Then %r{we should not even be polite about it} do
+ response.should_not have_tag("div.error,div.notice", RE_POLITENESS)
+ end
+
+ #
+ # Resource's attributes
+ #
+ # "Then page should have the $resource's $attributes" is in resource_steps
+
+ # helpful debug step
+ Then "we dump the response" do
+ dump_response
+ end
+end
+
+
+def have_flash notice, *args
+ have_tag("div.#{notice}", *args)
+end
+
+RE_PRETTY_RESOURCE = /the (index|show|new|create|edit|update|destroy) (\w+) (page|form)/i
+RE_THE_FOO_PAGE = /the '?([^']*)'? (page|form)/i
+RE_QUOTED_PATH = /^'([^']*)'$/i
+def grok_path path
+ path.gsub(/\s+again$/,'') # strip trailing ' again'
+ case
+ when path == 'the home page' then dest = '/'
+ when path =~ RE_PRETTY_RESOURCE then dest = template_for $1, $2
+ when path =~ RE_THE_FOO_PAGE then dest = $1
+ when path =~ RE_QUOTED_PATH then dest = $1
+ else dest = path
+ end
+ dest
+end
+
+# turns 'new', 'road bikes' into 'road_bikes/new'
+# note that it's "action resource"
+def template_for(action, resource)
+ "#{resource.gsub(" ","_")}/#{action}"
+end
+
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/user_steps.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/user_steps.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/steps/user_steps.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,153 @@
+require File.dirname(__FILE__) + '/../helper'
+
+RE_<%= file_name.capitalize %> = %r{(?:(?:the )? *(\w+) *)}
+RE_<%= file_name.capitalize %>_TYPE = %r{(?: *(\w+)? *)}
+steps_for(:<%= file_name %>) do
+
+ #
+ # Setting
+ #
+
+ Given "an anonymous <%= file_name %>" do
+ log_out!
+ end
+
+ Given "$an $<%= file_name %>_type <%= file_name %> with $attributes" do |_, <%= file_name %>_type, attributes|
+ create_<%= file_name %>! <%= file_name %>_type, attributes.to_hash_from_story
+ end
+
+ Given "$an $<%= file_name %>_type <%= file_name %> named '$login'" do |_, <%= file_name %>_type, login|
+ create_<%= file_name %>! <%= file_name %>_type, named_<%= file_name %>(login)
+ end
+
+ Given "$an $<%= file_name %>_type <%= file_name %> logged in as '$login'" do |_, <%= file_name %>_type, login|
+ create_<%= file_name %>! <%= file_name %>_type, named_<%= file_name %>(login)
+ log_in_<%= file_name %>!
+ end
+
+ Given "$actor is logged in" do |_, login|
+ log_in_<%= file_name %>! @<%= file_name %>_params || named_<%= file_name %>(login)
+ end
+
+ Given "there is no $<%= file_name %>_type <%= file_name %> named '$login'" do |_, login|
+ @<%= file_name %> = <%= class_name %>.find_by_login(login)
+ @<%= file_name %>.destroy! if @<%= file_name %>
+ @<%= file_name %>.should be_nil
+ end
+
+ #
+ # Actions
+ #
+ When "$actor logs out" do
+ log_out
+ end
+
+ When "$actor registers an account as the preloaded '$login'" do |_, login|
+ <%= file_name %> = named_<%= file_name %>(login)
+ <%= file_name %>['password_confirmation'] = <%= file_name %>['password']
+ create_<%= file_name %> <%= file_name %>
+ end
+
+ When "$actor registers an account with $attributes" do |_, attributes|
+ create_<%= file_name %> attributes.to_hash_from_story
+ end
+<% if options[:include_activation] %>
+ When "$actor activates with activation code $attributes" do |_, activation_code|
+ activation_code = '' if activation_code == 'that is blank'
+ activate
+ end<% end %>
+
+ When "$actor logs in with $attributes" do |_, attributes|
+ log_in_<%= file_name %> attributes.to_hash_from_story
+ end
+
+ #
+ # Result
+ #
+ Then "$actor should be invited to sign in" do |_|
+ response.should render_template('/<%= controller_file_path %>/new')
+ end
+
+ Then "$actor should not be logged in" do |_|
+ controller.logged_in?.should_not be_true
+ end
+
+ Then "$login should be logged in" do |login|
+ controller.logged_in?.should be_true
+ controller.current_<%= file_name %>.should === @<%= file_name %>
+ controller.current_<%= file_name %>.login.should == login
+ end
+
+end
+
+def named_<%= file_name %> login
+ <%= file_name %>_params = {
+ 'admin' => {'id' => 1, 'login' => 'addie', 'password' => '1234addie', 'email' => 'admin at example.com', },
+ 'oona' => { 'login' => 'oona', 'password' => '1234oona', 'email' => 'unactivated at example.com'},
+ 'reggie' => { 'login' => 'reggie', 'password' => 'monkey', 'email' => 'registered at example.com' },
+ }
+ <%= file_name %>_params[login.downcase]
+end
+
+#
+# <%= class_name %> account actions.
+#
+# The ! methods are 'just get the job done'. It's true, they do some testing of
+# their own -- thus un-DRY'ing tests that do and should live in the <%= file_name %> account
+# stories -- but the repetition is ultimately important so that a faulty test setup
+# fails early.
+#
+
+def log_out
+ get '/<%= controller_file_path %>/destroy'
+end
+
+def log_out!
+ log_out
+ response.should redirect_to('/')
+ follow_redirect!
+end
+
+def create_<%= file_name %>(<%= file_name %>_params={})
+ @<%= file_name %>_params ||= <%= file_name %>_params
+ post "/<%= model_controller_file_path %>", :<%= file_name %> => <%= file_name %>_params
+ @<%= file_name %> = <%= class_name %>.find_by_login(<%= file_name %>_params['login'])
+end
+
+def create_<%= file_name %>!(<%= file_name %>_type, <%= file_name %>_params)
+ <%= file_name %>_params['password_confirmation'] ||= <%= file_name %>_params['password'] ||= <%= file_name %>_params['password']
+ create_<%= file_name %> <%= file_name %>_params
+ response.should redirect_to('/')
+ follow_redirect!
+<% if options[:include_activation] %>
+ # fix the <%= file_name %>'s activation status
+ activate_<%= file_name %>! if <%= file_name %>_type == 'activated'<% end %>
+end
+
+<% if options[:include_activation] %>
+def activate_<%= file_name %> activation_code=nil
+ activation_code = @<%= file_name %>.activation_code if activation_code.nil?
+ get "/activate/#{activation_code}"
+end
+
+def activate_<%= file_name %>! *args
+ activate_<%= file_name %> *args
+ response.should redirect_to('/login')
+ follow_redirect!
+ response.should have_flash("notice", /Signup complete!/)
+end<% end %>
+
+def log_in_<%= file_name %> <%= file_name %>_params=nil
+ @<%= file_name %>_params ||= <%= file_name %>_params
+ <%= file_name %>_params ||= @<%= file_name %>_params
+ post "/<%= controller_routing_path %>", <%= file_name %>_params
+ @<%= file_name %> = <%= class_name %>.find_by_login(<%= file_name %>_params['login'])
+ controller.current_<%= file_name %>
+end
+
+def log_in_<%= file_name %>! *args
+ log_in_<%= file_name %> *args
+ response.should redirect_to('/')
+ follow_redirect!
+ response.should have_flash("notice", /Logged in successfully/)
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/accounts.story
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/accounts.story (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/accounts.story 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,186 @@
+Visitors should be in control of creating an account and of proving their
+essential humanity/accountability or whatever it is people think the
+id-validation does. We should be fairly skeptical about this process, as the
+identity+trust chain starts here.
+
+Story: Creating an account
+ As an anonymous <%= file_name %>
+ I want to be able to create an account
+ So that I can be one of the cool kids
+
+ #
+ # Account Creation: Get entry form
+ #
+ Scenario: Anonymous <%= file_name %> can start creating an account
+ Given an anonymous <%= file_name %>
+ When she goes to /signup
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And the page should look AWESOME
+ And she should see a <form> containing a textfield: Login, textfield: Email, password: Password, password: 'Confirm Password', submit: 'Sign up'
+
+ #
+ # Account Creation
+ #
+ Scenario: Anonymous <%= file_name %> can create an account
+ Given an anonymous <%= file_name %>
+ And no <%= file_name %> with login: 'Oona' exists
+ When she registers an account as the preloaded 'Oona'
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Thanks for signing up!'
+ And a <%= file_name %> with login: 'oona' should exist
+ And the <%= file_name %> should have login: 'oona', and email: 'unactivated at example.com'
+<% if options[:include_activation] %>
+ And the <%= file_name %>'s activation_code should not be nil
+ And the <%= file_name %>'s activated_at should be nil
+ And she should not be logged in
+<% else %>
+ And oona should be logged in
+<% end %>
+
+ #
+ # Account Creation Failure: Account exists
+ #
+<% if options[:include_activation] %>
+ Scenario: Anonymous <%= file_name %> can not create an account replacing a non-activated account
+ Given an anonymous <%= file_name %>
+ And a registered <%= file_name %> named 'Reggie'
+ And the <%= file_name %> has activation_code: 'activate_me', activated_at: nil!
+ And we try hard to remember the <%= file_name %>'s updated_at, and created_at
+ When she registers an account with login: 'reggie', password: 'monkey', and email: 'different at example.com'
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Login has already been taken'
+ And she should not see an errorExplanation message 'Email has already been taken'
+ And a <%= file_name %> with login: 'reggie' should exist
+ And the <%= file_name %> should have email: 'registered at example.com'
+ And the <%= file_name %>'s activation_code should not be nil
+ And the <%= file_name %>'s activated_at should be nil
+ And the <%= file_name %>'s created_at should stay the same under to_s
+ And the <%= file_name %>'s updated_at should stay the same under to_s
+ And she should not be logged in<% end %>
+
+ Scenario: Anonymous <%= file_name %> can not create an account replacing an activated account
+ Given an anonymous <%= file_name %>
+ And an activated <%= file_name %> named 'Reggie'
+ And we try hard to remember the <%= file_name %>'s updated_at, and created_at
+ When she registers an account with login: 'reggie', password: 'monkey', and email: 'reggie at example.com'
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Login has already been taken'
+ And she should not see an errorExplanation message 'Email has already been taken'
+ And a <%= file_name %> with login: 'reggie' should exist
+ And the <%= file_name %> should have email: 'registered at example.com'
+<% if options[:include_activation] %>
+ And the <%= file_name %>'s activation_code should be nil
+ And the <%= file_name %>'s activated_at should not be nil<% end %>
+ And the <%= file_name %>'s created_at should stay the same under to_s
+ And the <%= file_name %>'s updated_at should stay the same under to_s
+ And she should not be logged in
+
+ #
+ # Account Creation Failure: Incomplete input
+ #
+ Scenario: Anonymous <%= file_name %> can not create an account with incomplete or incorrect input
+ Given an anonymous <%= file_name %>
+ And no <%= file_name %> with login: 'Oona' exists
+ When she registers an account with login: '', password: 'monkey', password_confirmation: 'monkey' and email: 'unactivated at example.com'
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Login can't be blank'
+ And no <%= file_name %> with login: 'oona' should exist
+
+ Scenario: Anonymous <%= file_name %> can not create an account with no password
+ Given an anonymous <%= file_name %>
+ And no <%= file_name %> with login: 'Oona' exists
+ When she registers an account with login: 'oona', password: '', password_confirmation: 'monkey' and email: 'unactivated at example.com'
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Password can't be blank'
+ And no <%= file_name %> with login: 'oona' should exist
+
+ Scenario: Anonymous <%= file_name %> can not create an account with no password_confirmation
+ Given an anonymous <%= file_name %>
+ And no <%= file_name %> with login: 'Oona' exists
+ When she registers an account with login: 'oona', password: 'monkey', password_confirmation: '' and email: 'unactivated at example.com'
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Password confirmation can't be blank'
+ And no <%= file_name %> with login: 'oona' should exist
+
+ Scenario: Anonymous <%= file_name %> can not create an account with mismatched password & password_confirmation
+ Given an anonymous <%= file_name %>
+ And no <%= file_name %> with login: 'Oona' exists
+ When she registers an account with login: 'oona', password: 'monkey', password_confirmation: 'monkeY' and email: 'unactivated at example.com'
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Password doesn't match confirmation'
+ And no <%= file_name %> with login: 'oona' should exist
+
+ Scenario: Anonymous <%= file_name %> can not create an account with bad email
+ Given an anonymous <%= file_name %>
+ And no <%= file_name %> with login: 'Oona' exists
+ When she registers an account with login: 'oona', password: 'monkey', password_confirmation: 'monkey' and email: ''
+ Then she should be at the '<%= model_controller_routing_path %>/new' page
+ And she should see an errorExplanation message 'Email can't be blank'
+ And no <%= file_name %> with login: 'oona' should exist
+ When she registers an account with login: 'oona', password: 'monkey', password_confirmation: 'monkey' and email: 'unactivated at example.com'
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Thanks for signing up!'
+ And a <%= file_name %> with login: 'oona' should exist
+ And the <%= file_name %> should have login: 'oona', and email: 'unactivated at example.com'
+<% if options[:include_activation] %>
+ And the <%= file_name %>'s activation_code should not be nil
+ And the <%= file_name %>'s activated_at should be nil
+ And she should not be logged in
+<% else %>
+ And oona should be logged in
+<% end %>
+
+<% if options[:include_activation] %>
+Story: Activating an account
+ As a registered, but not yet activated, <%= file_name %>
+ I want to be able to activate my account
+ So that I can log in to the site
+
+ #
+ # Successful activation
+ #
+ Scenario: Not-yet-activated <%= file_name %> can activate her account
+ Given a registered <%= file_name %> named 'Reggie'
+ And the <%= file_name %> has activation_code: 'activate_me', activated_at: nil!
+ And we try hard to remember the <%= file_name %>'s updated_at, and created_at
+ When she goes to /activate/activate_me
+ Then she should be redirected to 'login'
+ When she follows that redirect!
+ Then she should see a notice message 'Signup complete!'
+ And a <%= file_name %> with login: 'reggie' should exist
+ And the <%= file_name %> should have login: 'reggie', and email: 'registered at example.com'
+ And the <%= file_name %>'s activation_code should be nil
+ And the <%= file_name %>'s activated_at should not be nil
+ And she should not be logged in
+
+ #
+ # Unsuccessful activation
+ #
+ Scenario: Not-yet-activated <%= file_name %> can't activate her account with a blank activation code
+ Given a registered <%= file_name %> named 'Reggie'
+ And the <%= file_name %> has activation_code: 'activate_me', activated_at: nil!
+ And we try hard to remember the <%= file_name %>'s updated_at, and created_at
+ When she goes to /activate/
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see an error message 'activation code was missing'
+ And a <%= file_name %> with login: 'reggie' should exist
+ And the <%= file_name %> should have login: 'reggie', activation_code: 'activate_me', and activated_at: nil!
+ And the <%= file_name %>'s updated_at should stay the same under to_s
+ And she should not be logged in
+
+ Scenario: Not-yet-activated <%= file_name %> can't activate her account with a bogus activation code
+ Given a registered <%= file_name %> named 'Reggie'
+ And the <%= file_name %> has activation_code: 'activate_me', activated_at: nil!
+ And we try hard to remember the <%= file_name %>'s updated_at, and created_at
+ When she goes to /activate/i_haxxor_joo
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see an error message 'couldn\'t find a <%= file_name %> with that activation code'
+ And a <%= file_name %> with login: 'reggie' should exist
+ And the <%= file_name %> should have login: 'reggie', activation_code: 'activate_me', and activated_at: nil!
+ And the <%= file_name %>'s updated_at should stay the same under to_s
+ And she should not be logged in
+<% end %>
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/sessions.story
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/sessions.story (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/stories/users/sessions.story 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,134 @@
+Users want to know that nobody can masquerade as them. We want to extend trust
+only to visitors who present the appropriate credentials. Everyone wants this
+identity verification to be as secure and convenient as possible.
+
+Story: Logging in
+ As an anonymous <%= file_name %> with an account
+ I want to log in to my account
+ So that I can be myself
+
+ #
+ # Log in: get form
+ #
+ Scenario: Anonymous <%= file_name %> can get a login form.
+ Given an anonymous <%= file_name %>
+ When she goes to /login
+ Then she should be at the new <%= controller_file_name %> page
+ And the page should look AWESOME
+ And she should see a <form> containing a textfield: Login, password: Password, and submit: 'Log in'
+
+ #
+ # Log in successfully, but don't remember me
+ #
+ Scenario: Anonymous <%= file_name %> can log in
+ Given an anonymous <%= file_name %>
+ And an activated <%= file_name %> named 'reggie'
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: 'monkey', remember me: ''
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Logged in successfully'
+ And reggie should be logged in
+ And she should not have an auth_token cookie
+
+ Scenario: Logged-in <%= file_name %> who logs in should be the new one
+ Given an activated <%= file_name %> named 'reggie'
+ And an activated <%= file_name %> logged in as 'oona'
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: 'monkey', remember me: ''
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Logged in successfully'
+ And reggie should be logged in
+ And she should not have an auth_token cookie
+
+ #
+ # Log in successfully, remember me
+ #
+ Scenario: Anonymous <%= file_name %> can log in and be remembered
+ Given an anonymous <%= file_name %>
+ And an activated <%= file_name %> named 'reggie'
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: 'monkey', remember me: '1'
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Logged in successfully'
+ And reggie should be logged in
+ And she should have an auth_token cookie
+ # assumes fixtures were run sometime
+ And her session store should have <%= file_name %>_id: 4
+
+ #
+ # Log in unsuccessfully
+ #
+
+ Scenario: Logged-in <%= file_name %> who fails logs in should be logged out
+ Given an activated <%= file_name %> named 'oona'
+ When she creates a singular <%= controller_file_name %> with login: 'oona', password: '1234oona', remember me: '1'
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Logged in successfully'
+ And oona should be logged in
+ And she should have an auth_token cookie
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: 'i_haxxor_joo'
+ Then she should be at the new <%= controller_file_name %> page
+ Then she should see an error message 'Couldn't log you in as 'reggie''
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
+
+ Scenario: Log-in with bogus info should fail until it doesn't
+ Given an activated <%= file_name %> named 'reggie'
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: 'i_haxxor_joo'
+ Then she should be at the new <%= controller_file_name %> page
+ Then she should see an error message 'Couldn't log you in as 'reggie''
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: ''
+ Then she should be at the new <%= controller_file_name %> page
+ Then she should see an error message 'Couldn't log you in as 'reggie''
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
+ When she creates a singular <%= controller_file_name %> with login: '', password: 'monkey'
+ Then she should be at the new <%= controller_file_name %> page
+ Then she should see an error message 'Couldn't log you in as '''
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
+ When she creates a singular <%= controller_file_name %> with login: 'leonard_shelby', password: 'monkey'
+ Then she should be at the new <%= controller_file_name %> page
+ Then she should see an error message 'Couldn't log you in as 'leonard_shelby''
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
+ When she creates a singular <%= controller_file_name %> with login: 'reggie', password: 'monkey', remember me: '1'
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'Logged in successfully'
+ And reggie should be logged in
+ And she should have an auth_token cookie
+ # assumes fixtures were run sometime
+ And her session store should have <%= file_name %>_id: 4
+
+
+ #
+ # Log out successfully (should always succeed)
+ #
+ Scenario: Anonymous (logged out) <%= file_name %> can log out.
+ Given an anonymous <%= file_name %>
+ When she goes to /logout
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'You have been logged out'
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
+
+ Scenario: Logged in <%= file_name %> can log out.
+ Given an activated <%= file_name %> logged in as 'reggie'
+ When she goes to /logout
+ Then she should be redirected to the home page
+ When she follows that redirect!
+ Then she should see a notice message 'You have been logged out'
+ And she should not be logged in
+ And she should not have an auth_token cookie
+ And her session store should not have <%= file_name %>_id
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/functional_test.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/functional_test.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/functional_test.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,88 @@
+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 => 'monkey'
+ assert session[:<%= file_name %>_id]
+ assert_response :redirect
+ end
+
+ def test_should_fail_login_and_not_redirect
+ post :create, :login => 'quentin', :password => 'bad password'
+ assert_nil session[:<%= file_name %>_id]
+ assert_response :success
+ end
+
+ def test_should_logout
+ login_as :quentin
+ get :destroy
+ assert_nil session[:<%= file_name %>_id]
+ assert_response :redirect
+ end
+
+ def test_should_remember_me
+ @request.cookies["auth_token"] = nil
+ post :create, :login => 'quentin', :password => 'monkey', :remember_me => "1"
+ assert_not_nil @response.cookies["auth_token"]
+ end
+
+ def test_should_not_remember_me
+ @request.cookies["auth_token"] = nil
+ post :create, :login => 'quentin', :password => 'monkey', :remember_me => "0"
+ puts @response.cookies["auth_token"]
+ assert @response.cookies["auth_token"].blank?
+ end
+
+ def test_should_delete_token_on_logout
+ login_as :quentin
+ get :destroy
+ assert @response.cookies["auth_token"].blank?
+ 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_expired_cookie_login
+ <%= table_name %>(:quentin).remember_me
+ <%= table_name %>(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
+ @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
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/mailer_test.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/mailer_test.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/mailer_test.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,31 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require '<%= file_name %>_mailer'
+
+class <%= class_name %>MailerTest < Test::Unit::TestCase
+ FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
+ CHARSET = "utf-8"
+
+ include ActionMailer::Quoting
+
+ def setup
+ ActionMailer::Base.delivery_method = :test
+ ActionMailer::Base.perform_deliveries = true
+ ActionMailer::Base.deliveries = []
+
+ @expected = TMail::Mail.new
+ @expected.set_content_type "text", "plain", { "charset" => CHARSET }
+ end
+
+ def test_dummy_test
+ #do nothing
+ end
+
+ private
+ def read_fixture(action)
+ IO.readlines("#{FIXTURES_PATH}/<%= file_name %>_mailer/#{action}")
+ end
+
+ def encode(subject)
+ quoted_printable(subject, CHARSET)
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/model_functional_test.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/model_functional_test.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/model_functional_test.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,99 @@
+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
+ <% if options[:stateful] %>
+ def test_should_sign_up_user_in_pending_state
+ create_<%= file_name %>
+ assigns(:<%= file_name %>).reload
+ assert assigns(:<%= file_name %>).pending?
+ end<% end %>
+
+ <% if options[:include_activation] %>
+ def test_should_sign_up_user_with_activation_code
+ create_<%= file_name %>
+ assigns(:<%= file_name %>).reload
+ assert_not_nil assigns(:<%= file_name %>).activation_code
+ end
+
+ def test_should_activate_user
+ assert_nil <%= class_name %>.authenticate('aaron', 'test')
+ get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
+ assert_redirected_to '/<%= controller_routing_path %>/new'
+ assert_not_nil flash[:notice]
+ assert_equal <%= table_name %>(:aaron), <%= class_name %>.authenticate('aaron', 'monkey')
+ end
+
+ def test_should_not_activate_user_without_key
+ get :activate
+ assert_nil flash[:notice]
+ rescue ActionController::RoutingError
+ # in the event your routes deny this, we'll just bow out gracefully.
+ end
+
+ def test_should_not_activate_user_with_blank_key
+ get :activate, :activation_code => ''
+ assert_nil flash[:notice]
+ rescue ActionController::RoutingError
+ # well played, sir
+ end<% end %>
+
+ protected
+ def create_<%= file_name %>(options = {})
+ post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire at example.com',
+ :password => 'quire69', :password_confirmation => 'quire69' }.merge(options)
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/unit_test.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/unit_test.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/generators/authenticated/templates/test/unit_test.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,164 @@
+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
+ <%= file_name %> = create_<%= file_name %>
+ assert !<%= file_name %>.new_record?, "#{<%= file_name %>.errors.full_messages.to_sentence}"
+ end
+ end
+<% if options[:include_activation] %>
+ def test_should_initialize_activation_code_upon_creation
+ <%= file_name %> = create_<%= file_name %>
+ <%= file_name %>.reload
+ assert_not_nil <%= file_name %>.activation_code
+ end
+<% end %><% if options[:stateful] %>
+ def test_should_create_and_start_in_pending_state
+ <%= file_name %> = create_<%= file_name %>
+ <%= file_name %>.reload
+ assert <%= file_name %>.pending?
+ 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', 'monkey')
+ end
+
+ def test_should_authenticate_<%= file_name %>
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'monkey')
+ 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
+
+ def test_should_remember_me_for_one_week
+ before = 1.week.from_now.utc
+ <%= table_name %>(:quentin).remember_me_for 1.week
+ after = 1.week.from_now.utc
+ assert_not_nil <%= table_name %>(:quentin).remember_token
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
+ assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
+ end
+
+ def test_should_remember_me_until_one_week
+ time = 1.week.from_now.utc
+ <%= table_name %>(:quentin).remember_me_until time
+ assert_not_nil <%= table_name %>(:quentin).remember_token
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
+ assert_equal <%= table_name %>(:quentin).remember_token_expires_at, time
+ end
+
+ def test_should_remember_me_default_two_weeks
+ before = 2.weeks.from_now.utc
+ <%= table_name %>(:quentin).remember_me
+ after = 2.weeks.from_now.utc
+ assert_not_nil <%= table_name %>(:quentin).remember_token
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
+ assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
+ end
+<% if options[:stateful] %>
+ def test_should_register_passive_<%= file_name %>
+ <%= file_name %> = create_<%= file_name %>(:password => nil, :password_confirmation => nil)
+ assert <%= file_name %>.passive?
+ <%= file_name %>.update_attributes(:password => 'new password', :password_confirmation => 'new password')
+ <%= file_name %>.register!
+ assert <%= file_name %>.pending?
+ end
+
+ def test_should_suspend_<%= file_name %>
+ <%= table_name %>(:quentin).suspend!
+ assert <%= table_name %>(:quentin).suspended?
+ end
+
+ def test_suspended_<%= file_name %>_should_not_authenticate
+ <%= table_name %>(:quentin).suspend!
+ assert_not_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
+ end
+
+ def test_should_unsuspend_<%= file_name %>_to_active_state
+ <%= table_name %>(:quentin).suspend!
+ assert <%= table_name %>(:quentin).suspended?
+ <%= table_name %>(:quentin).unsuspend!
+ assert <%= table_name %>(:quentin).active?
+ end
+
+ def test_should_unsuspend_<%= file_name %>_with_nil_activation_code_and_activated_at_to_passive_state
+ <%= table_name %>(:quentin).suspend!
+ <%= class_name %>.update_all :activation_code => nil, :activated_at => nil
+ assert <%= table_name %>(:quentin).suspended?
+ <%= table_name %>(:quentin).reload.unsuspend!
+ assert <%= table_name %>(:quentin).passive?
+ end
+
+ def test_should_unsuspend_<%= file_name %>_with_activation_code_and_nil_activated_at_to_pending_state
+ <%= table_name %>(:quentin).suspend!
+ <%= class_name %>.update_all :activation_code => 'foo-bar', :activated_at => nil
+ assert <%= table_name %>(:quentin).suspended?
+ <%= table_name %>(:quentin).reload.unsuspend!
+ assert <%= table_name %>(:quentin).pending?
+ end
+
+ def test_should_delete_<%= file_name %>
+ assert_nil <%= table_name %>(:quentin).deleted_at
+ <%= table_name %>(:quentin).delete!
+ assert_not_nil <%= table_name %>(:quentin).deleted_at
+ assert <%= table_name %>(:quentin).deleted?
+ end
+<% end %>
+protected
+ def create_<%= file_name %>(options = {})
+ record = <%= class_name %>.new({ :login => 'quire', :email => 'quire at example.com', :password => 'quire69', :password_confirmation => 'quire69' }.merge(options))
+ record.<% if options[:stateful] %>register! if record.valid?<% else %>save<% end %>
+ record
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/init.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/init.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/init.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,3 @@
+require File.dirname(__FILE__) + '/lib/authentication'
+require File.dirname(__FILE__) + '/lib/authentication/by_password'
+require File.dirname(__FILE__) + '/lib/authentication/by_cookie_token'
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/install.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/install.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/install.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1 @@
+puts IO.read(File.join(File.dirname(__FILE__), 'README'))
\ No newline at end of file
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_cookie_token.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_cookie_token.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_cookie_token.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,85 @@
+# -*- coding: mule-utf-8 -*-
+module Authentication
+ module ByCookieToken
+ # Stuff directives into including module
+ def self.included( recipient )
+ recipient.extend( ModelClassMethods )
+ recipient.class_eval do
+ include ModelInstanceMethods
+ end
+ end
+
+ #
+ # Class Methods
+ #
+ module ModelClassMethods
+ end # class methods
+
+ #
+ # Instance Methods
+ #
+ module ModelInstanceMethods
+ def remember_token?
+ (!remember_token.blank?) &&
+ remember_token_expires_at && (Time.now.utc < remember_token_expires_at.utc)
+ end
+
+ # These create and unset the fields required for remembering users between browser closes
+ def remember_me
+ remember_me_for 2.weeks
+ end
+
+ def remember_me_for(time)
+ remember_me_until time.from_now.utc
+ end
+
+ def remember_me_until(time)
+ self.remember_token_expires_at = time
+ self.remember_token = self.class.make_token
+ save(false)
+ end
+
+ # refresh token (keeping same expires_at) if it exists
+ def refresh_token
+ if remember_token?
+ self.remember_token = self.class.make_token
+ save(false)
+ end
+ end
+
+ #
+ # Deletes the server-side record of the authentication token. The
+ # client-side (browser cookie) and server-side (this remember_token) must
+ # always be deleted together.
+ #
+ def forget_me
+ self.remember_token_expires_at = nil
+ self.remember_token = nil
+ save(false)
+ end
+ end # instance methods
+ end
+
+
+ #
+ #
+ module ByCookieTokenController
+ # Stuff directives into including module
+ def self.included( recipient )
+ recipient.extend( ControllerClassMethods )
+ recipient.class_eval do
+ include ControllerInstanceMethods
+ end
+ end
+
+ #
+ # Class Methods
+ #
+ module ControllerClassMethods
+ end # class methods
+
+ module ControllerInstanceMethods
+ end # instance methods
+ end
+end
+
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_password.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_password.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication/by_password.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,65 @@
+module Authentication
+ module ByPassword
+
+ # Stuff directives into including module
+ def self.included( recipient )
+ recipient.extend( ModelClassMethods )
+ recipient.class_eval do
+ include ModelInstanceMethods
+
+ # Virtual attribute for the unencrypted password
+ attr_accessor :password
+ validates_presence_of :password, :if => :password_required?
+ validates_presence_of :password_confirmation, :if => :password_required?
+ validates_confirmation_of :password, :if => :password_required?
+ validates_length_of :password, :within => 6..40, :if => :password_required?
+ before_save :encrypt_password
+ end
+ end # #included directives
+
+ #
+ # Class Methods
+ #
+ module ModelClassMethods
+ # This provides a modest increased defense against a dictionary attack if
+ # your db were ever compromised, but will invalidate existing passwords.
+ # See the README and the file config/initializers/site_keys.rb
+ #
+ # It may not be obvious, but if you set REST_AUTH_SITE_KEY to nil and
+ # REST_AUTH_DIGEST_STRETCHES to 1 you'll have backwards compatibility with
+ # older versions of restful-authentication.
+ def password_digest(password, salt)
+ digest = REST_AUTH_SITE_KEY
+ REST_AUTH_DIGEST_STRETCHES.times do
+ digest = secure_digest(digest, salt, password, REST_AUTH_SITE_KEY)
+ end
+ digest
+ end
+ end # class methods
+
+ #
+ # Instance Methods
+ #
+ module ModelInstanceMethods
+
+ # Encrypts the password with the user salt
+ def encrypt(password)
+ self.class.password_digest(password, salt)
+ end
+
+ def authenticated?(password)
+ crypted_password == encrypt(password)
+ end
+
+ # before filter
+ def encrypt_password
+ return if password.blank?
+ self.salt = self.class.make_token if new_record?
+ self.crypted_password = encrypt(password)
+ end
+ def password_required?
+ crypted_password.blank? || !password.blank?
+ end
+ end # instance methods
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authentication.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,43 @@
+module Authentication
+ unless defined? CONSTANTS_DEFINED
+ # Uncomment to suit
+ RE_LOGIN_OK = /\A\w[\w\.\-_@]+\z/ # ASCII, strict
+ # RE_LOGIN_OK = /\A[[:alnum:]][[:alnum:]\.\-_@]+\z/ # Unicode, strict
+ # RE_LOGIN_OK = /\A[^[:cntrl:]\\<>\/&]*\z/ # Unicode, permissive
+ MSG_LOGIN_BAD = "use only letters, numbers, and .-_@ please."
+
+ RE_NAME_OK = /\A[^[:cntrl:]\\<>\/&]*\z/ # Unicode, permissive
+ MSG_NAME_BAD = "avoid non-printing characters and \\><&/ please."
+
+ # This is purposefully imperfect -- it's just a check for bogus input. See
+ # http://www.regular-expressions.info/email.html
+ RE_EMAIL_NAME = '[\w\.%\+\-]+' # what you actually see in practice
+ #RE_EMAIL_NAME = '0-9A-Z!#\$%\&\'\*\+_/=\?^\-`\{|\}~\.' # technically allowed by RFC-2822
+ RE_DOMAIN_HEAD = '(?:[A-Z0-9\-]+\.)+'
+ RE_DOMAIN_TLD = '(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)'
+ RE_EMAIL_OK = /\A#{RE_EMAIL_NAME}@#{RE_DOMAIN_HEAD}#{RE_DOMAIN_TLD}\z/i
+ MSG_EMAIL_BAD = "should look like an email address."
+
+ CONSTANTS_DEFINED = 'yup' # sorry for the C idiom
+ end
+
+ def self.included( recipient )
+ recipient.extend( ModelClassMethods )
+ recipient.class_eval do
+ include ModelInstanceMethods
+ end
+ end
+
+ module ModelClassMethods
+ def secure_digest(*args)
+ Digest::SHA1.hexdigest(args.flatten.join('--'))
+ end
+ def make_token
+ secure_digest(Time.now, (1..10).map{ rand.to_s })
+ end
+ end # class methods
+
+ module ModelInstanceMethods
+ end # instance methods
+
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/aasm_roles.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/aasm_roles.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/aasm_roles.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,64 @@
+module Authorization
+ module AasmRoles
+ unless Object.constants.include? "STATEFUL_ROLES_CONSTANTS_DEFINED"
+ STATEFUL_ROLES_CONSTANTS_DEFINED = 'yup' # sorry for the C idiom
+ end
+
+ def self.included( recipient )
+ recipient.extend( StatefulRolesClassMethods )
+ recipient.class_eval do
+ include StatefulRolesInstanceMethods
+ include AASM
+ aasm_column :state
+ aasm_initial_state :initial => :pending
+ aasm_state :passive
+ aasm_state :pending, :enter => :make_activation_code
+ aasm_state :active, :enter => :do_activate
+ aasm_state :suspended
+ aasm_state :deleted, :enter => :do_delete
+
+ aasm_event :register do
+ transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) }
+ end
+
+ aasm_event :activate do
+ transitions :from => :pending, :to => :active
+ end
+
+ aasm_event :suspend do
+ transitions :from => [:passive, :pending, :active], :to => :suspended
+ end
+
+ aasm_event :delete do
+ transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
+ end
+
+ aasm_event :unsuspend do
+ transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| !u.activated_at.blank? }
+ transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| !u.activation_code.blank? }
+ transitions :from => :suspended, :to => :passive
+ end
+ end
+ end
+
+ module StatefulRolesClassMethods
+
+ end # class methods
+
+ module StatefulRolesInstanceMethods
+ # Returns true if the user has just been activated.
+ def recently_activated?
+ @activated
+ end
+ def do_delete
+ self.deleted_at = Time.now.utc
+ end
+
+ def do_activate
+ @activated = true
+ self.activated_at = Time.now.utc
+ self.deleted_at = self.activation_code = nil
+ end
+ end # instance methods
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/stateful_roles.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/stateful_roles.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization/stateful_roles.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,63 @@
+module Authorization
+ module StatefulRoles
+ unless Object.constants.include? "STATEFUL_ROLES_CONSTANTS_DEFINED"
+ STATEFUL_ROLES_CONSTANTS_DEFINED = 'yup' # sorry for the C idiom
+ end
+
+ def self.included( recipient )
+ recipient.extend( StatefulRolesClassMethods )
+ recipient.class_eval do
+ include StatefulRolesInstanceMethods
+
+ acts_as_state_machine :initial => :pending
+ state :passive
+ state :pending, :enter => :make_activation_code
+ state :active, :enter => :do_activate
+ state :suspended
+ state :deleted, :enter => :do_delete
+
+ event :register do
+ transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) }
+ end
+
+ event :activate do
+ transitions :from => :pending, :to => :active
+ end
+
+ event :suspend do
+ transitions :from => [:passive, :pending, :active], :to => :suspended
+ end
+
+ event :delete do
+ transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
+ end
+
+ event :unsuspend do
+ transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| !u.activated_at.blank? }
+ transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| !u.activation_code.blank? }
+ transitions :from => :suspended, :to => :passive
+ end
+ end
+ end
+
+ module StatefulRolesClassMethods
+
+ end # class methods
+
+ module StatefulRolesInstanceMethods
+ # Returns true if the user has just been activated.
+ def recently_activated?
+ @activated
+ end
+ def do_delete
+ self.deleted_at = Time.now.utc
+ end
+
+ def do_activate
+ @activated = true
+ self.activated_at = Time.now.utc
+ self.deleted_at = self.activation_code = nil
+ end
+ end # instance methods
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/authorization.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,15 @@
+module Authorization
+
+ def self.included( recipient )
+ recipient.extend( ModelClassMethods )
+ recipient.class_eval do
+ include ModelInstanceMethods
+ end
+ end
+
+ module ModelClassMethods
+ end # class methods
+
+ module ModelInstanceMethods
+ end # instance methods
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification/email_validation.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification/email_validation.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification/email_validation.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,20 @@
+module Trustification
+ module EmailValidation
+ unless Object.constants.include? "CONSTANTS_DEFINED"
+ CONSTANTS_DEFINED = 'yup' # sorry for the C idiom
+ end
+
+ def self.included( recipient )
+ recipient.extend( ClassMethods )
+ recipient.class_eval do
+ include InstanceMethods
+ end
+ end
+
+ module ClassMethods
+ end # class methods
+
+ module InstanceMethods
+ end # instance methods
+ end
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification.rb
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification.rb (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/lib/trustification.rb 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,15 @@
+module Trustification
+
+ def self.included( recipient )
+ recipient.extend( ModelClassMethods )
+ recipient.class_eval do
+ include ModelInstanceMethods
+ end
+ end
+
+ module ModelClassMethods
+ end # class methods
+
+ module ModelInstanceMethods
+ end # instance methods
+end
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/AccessControl.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/AccessControl.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/AccessControl.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,2 @@
+
+See the notes in [[Authorization]]
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authentication.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authentication.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authentication.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,5 @@
+Guides to best practices:
+* "The OWASP Guide to Building Secure Web Applications":http://www.owasp.org/index.php/Category:OWASP_Guide_Project
+** specifically, of course, the chapter on Authentication.
+* "Secure Programming for Linux and Unix HOWTO":http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/web-authentication.html
+* "Authentication and Identification,":http://www.downes.ca/post/12 by Stephen Downes **Highly Recommended**
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authorization.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authorization.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Authorization.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,154 @@
+h2. Authorization
+
+"Best Practices for Authorization":http://www.owasp.org/index.php/Guide_to_Authorization
+# auth system should deny by default
+# Principle of least privilege (fine-grain)
+# each non-anonymous entry point have an access control check
+# authorization check at or near the beginning of code implementing sensitive activities
+# Ensure that Model code checks to ensure that the requesting user should have access to the protected resource.
+# Reauthorization for high value activities or after idle out
+# If custom code is required to perform authorization functions, consider
+ fail-safe authentication and exception handling – ensure that if an exception
+ is thrown, the user is logged out or at least prevented from accessing the
+ protected resource or function.
+# Well written applications centralize access control routines, so if any bugs
+ are found, they can be fixed once and the results apply throughout the
+ application immediately.
+
+h2. Authorization in a trust context
+
+* [http://en.wikipedia.org/wiki/Authorization]
+* remember: goal is **prediction** not **control**
+
+h2. Patterns for Policy definition / Authorization / access control
+
+*Reference Monitor (SecPatt p256)
+** Set of authorization rules
+** Actor, Action, Resource => Monitor+(rules) => ctrlr
+* Role based:
+ subj, role, right. action, resource
+ RBAC, access is controlled at the system level, outside of the user's control
+* Filter based
+ User x Controller x Action x Params --
+* Object based
+ model security delegation
+* Access Control Matrix http://en.wikipedia.org/wiki/Access_Control_Matrix
+* CommandProcessor pattern (DSL approach)
+* DENY FROM ... / ALLOW FROM ... approach
+* Capability based control: bundle together the designation of an object and the permission to access that object
+ ie. I can name it if and only if I am allowed to get at it.
+
+h2. Notes from "Security patterns":http://www.amazon.com/Security-Patterns-Integrating-Engineering-Software/dp/0470858842
+by M Schumacher ("website for book":http://www.securitypatterns.org/)
+
+Reference Monitor (SecPatt p256)
+ * Set of authorization rules
+ * Actor, Action, Resource => Monitor+(rules) => ctrlr
+
+= Full access with Errors (SecPatt p305)
+
+* Users should not be able to view data or perform operations for which they
+ have no permissions.
+* Hiding an available and accessible function is inappropriate, because users
+ must be able to see what they can do.
+* The visual appeal and usability of a graphical user interface (GUI) can be de-
+ graded by varying layouts depending on the (current) access rights of a
+ user. For example, blank space might appear for some users where others see
+ options they can access, or sequence and number of menu items might differ,
+ depending on the current user’s rights, and thus ‘blind’ operation of the menu
+ by an expe- rienced user is no longer possible.
+* Showing currently unavailable functions can tease users to into upgrading
+ their access rights, for example by paying for the access or buying a license
+ after us- ing a demo version.
+* Trial and error are ineffective means of learning which functions are
+ accessible. Invoking an operation only to learn that it doesn’t work with
+ your access rights is confusing.
+* The privilege grouping of the typical user community might not be known at the
+ design time of the GUI, and it might change over time, for example through
+ organizational or business process changes, so that providing a few special
+ modes of the GUI depending on the corresponding user roles is inappropriate.
+* Checking whether a function is allowed by a user is most efficient, robust and
+ secure, if done by the function itself—at least the code performing the checks
+ is then closely related to the code performing the subsequent operation
+ afterwards.
+
+h2. Outcomes / Obligations
+
+-- forbid
+-- ask for trust escalation (eg log in, prove_as_human, validate_email, are_you_sure, send_me_ten_cents)
+-- drag ass
+-- permit
+
+-- reinterpret past actions based on future evolution of trust
+-- prioritize changesets based on trust.
+
+
+h2. Notes from "Core Security Patterns":http://www.coresecuritypatterns.com/patterns.htm website
+
+# Authentication Enforcer who the hell are you
+# Intercepting Validator Is your request well-formed
+# Authorization Enforcer Are you allowed to do that
+# Secure Logger Know what's happening/happened
+#
+
+h2. notes from "XACML":http://www.nsitonline.in/hemant/stuff/xacml/XACML%20Tutorial%20with%20a%20Use%20Case%20Scenario%20for%20Academic%20Institutions.htm
+
+PolicySets [Policy Combining Algorithm]
+Policy [Rule Combining Algorithm] (defines access to particular resources.)
+# Target
+## Subject Attributes
+## Resource Attributes
+## Action Attributes
+## Environment Attributes
+# Rule [Effect] Identify various conditions or cases under which a policy may become applicable
+## Subject Attributes user who has made an access request
+## Resource Attributes object to which access is requested
+## Action Attributes action the subject wants to take on the resource
+## Environment Attributes request environment (time of day, ip, etc)
+## Conditions
+# Obligations
+
+Roles -- student, janitor, dean, stranger, ...
+Branches -- Departments, etc.
+
+* Examine applicable rules until you get an outcome, failure or passes thru (in which case rejected)
+* Rule combining Algorithms
+
+* Obligations -- things to do once requests have been denied or permitted
+
+Reference Monitor (SecPatt p256)
+ * Set of authorization rules
+ * Actor, Action, Resource --> Monitor+(rules) --> ctrlr
+
+#
+# ask for permissions on arbitrary (subject, action, resource)
+ * roles
+# get filtered object based on action (:public_info, :admin_info, etc.)
+# attach a rule to a (subject|role, action, resource) triple
+ "subject should have role R"
+ "subject should have role R on resource X"
+ "should meet the
+
+* Role supervisor:
+ * adds, defines, removes roles. no policy -- just attaches roles to users
+
+* Policy
+ answers "can Actor do Action to Resource"
+ * Rules
+ * Rule resolution
+ * outcome, obligations.
+ policy definitions can come from many places, go to policy mgr.
+* Hall monitor
+ enforces policy (before filters)
+* Policy observers
+ handle policy obligations
+
+* Athentication -- identification, really: securely attach visitor to identity
+* Validation -- qualify trust
+* Access control -- define policy
+** Roles
+** Acc. matrix
+* Authorization -- enforce policy (reference monitor ; filter chain)
+* Obligations
+** Audit -- (observer)
+
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/RailsPlugins.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/RailsPlugins.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/RailsPlugins.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,78 @@
+h1. Rails Authentication, Authorization and Access Control plugins
+
+h2. Authentication plugins
+
+* http://github.com/technoweenie/restful-authentication/tree/master -- the accepted standard for authentication
+* http://github.com/mrflip/restful-authentication/tree/master -- my fork of restful_authentication with more modularity, more specs and a few security tweaks
+* http://github.com/josh/open_id_authentication/tree/master -- OpenID authentication
+
+h2. Authorization plugins
+
+From
+* http://agilewebdevelopment.com/plugins/tag/security
+* http://www.vaporbase.com/postings/Authorization_in_Rails
+
+* http://github.com/jbarket/restful-authorization/tree/master
+
+* http://agilewebdevelopment.com/plugins/rolerequirement
+ http://code.google.com/p/rolerequirement/
+ http://rolerequirement.googlecode.com/svn/tags/role_requirement/
+ 9 votes
+
+* http://github.com/ezmobius/acl_system2/
+ http://agilewebdevelopment.com/plugins/acl_system
+ http://opensvn.csie.org/ezra/rails/plugins/dev/acl_system2/
+ last touched 2006
+ 57 votes on AWD
+ * also: http://agilewebdevelopment.com/plugins/acl_system2_ownership
+
+ bq. access_control [:new, :create, :update, :edit] => '(admin | user |
+ moderator)', :delete => 'admin'
+ <% restrict_to "(admin | moderator) & !blacklist" do %>
+ <%= link_to "Admin & Moderator only link", :action =>'foo' %>
+ <% end %>
+
+* Authorization Recipe (from Rails Recipes #32)
+ http://www.vaporbase.com/postings/Authorization_in_Rails
+ http://opensvn.csie.org/mabs29/plugins/simple_access_control
+
+* Active ACL
+ http://phpgacl.sourceforge.net/demo/phpgacl/docs/manual.html
+ (Access-matrix driven)
+
+* http://github.com/aiwilliams/access_controlled_system
+
+* http://agilewebdevelopment.com/plugins/access
+
+* http://robzon.aenima.pl/2007/12/base-auth-is-out.html
+ http://agilewebdevelopment.com/plugins/base_auth
+ http://base-auth.googlecode.com/svn/trunk/
+ 40 votes
+
+* http://agilewebdevelopment.com/plugins/authorization
+ http://www.writertopia.com/developers/authorization
+ http://github.com/DocSavage/rails-authorization-plugin/tree/master
+ Opaque policy descriptions
+ 19 votes
+
+* http://github.com/shuber/access_control_list/
+ Not much there yet
+
+* https://opensvn.csie.org/traccgi/tobionrails
+ http://agilewebdevelopment.com/plugins/access_control
+ http://opensvn.csie.org/tobionrails/plugins/access_control
+ last touched 1 year ago
+
+* http://github.com/mdarby/restful_acl/
+ -- google code too --
+ Just does REST? More of an app than a plugin.
+
+* http://github.com/stonean/lockdown/tree/master
+ http://lockdown.rubyforge.org
+ http://groups.google.com/group/stonean_lockdown?hl=en
+ "Lockdown stores an array of access rights in the session"
+
+h2. Trust / Validation etc. plugins
+
+
+* http://agilewebdevelopment.com/plugins/recaptcha
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityFramework.graffle
===================================================================
(Binary files differ)
Property changes on: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityFramework.graffle
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityFramework.png
===================================================================
(Binary files differ)
Property changes on: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityFramework.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityPatterns.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityPatterns.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/SecurityPatterns.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,163 @@
+h1. Security from the perspective of a community site.
+
+Better than anything you'll read below on the subject:
+
+* "The OWASP Guide to Building Secure Web Applications":http://www.owasp.org/index.php/Category:OWASP_Guide_Project
+* "Secure Programming for Linux and Unix HOWTO":http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/web-authentication.html
+* "Core Security Patterns":http://www.coresecuritypatterns.com/patterns.htm
+* Stephen Downes' article on "Authentication and Identification":http://www.downes.ca/post/12
+
+h2. Snazzy Diagram
+
+!http://github.com/technoweenie/restful-authentication/tree/master/notes/SecurityFramework.png?raw=true!:http://github.com/technoweenie/restful-authentication/tree/master/notes/SecurityFramework.png
+
+(in notes/SecurityFramework.png)
+
+h2. Terms
+
+* Identification: Assign this visitor a name and an associated identity
+ (picture, website, favorite pokemon, trust metric, security roles).
+
+ bq. "Behold. I am not Gandalf the Grey, whom you betrayed, I am Gandalf the White,
+ who has returned from death." -- Tolkien
+
+* Authentication: Verify this visitor matches the claimed identity.
+
+ bq. "My name is Werner Brandis. My voice is my password. Verify me." -- Sneakers
+
+* Authorization: Given a request (Actions+Resource+Environment), decide if it's safe.
+
+ bq. "Of every tree of the garden thou mayest freely eat: But of the tree of
+ the knowledge of good and evil, thou shalt not eat of it: for in the day that
+ thou eatest thereof thou shalt surely die." -- Gen 2:16-17
+
+* Trust: Confidence this visitor will act reliably.
+
+ bq. "A copper! A copper! How d'ya like that, boys? And we went for it. _I_ went
+ for it. Treated him like a kid brother. And I was gonna split fifty-fifty with
+ a copper." -- James Cagney, White Heat
+
+** Reputation from Trust Network: Award trust to this visitor based on what other trusted parties say.
+
+ bq. "He used my name? In the /street/? He called me a punk? My name was on
+ the street? When we bounce from this s-t here, Y'all gonna go down on them
+ corners, let the people know: word did not get back to me. Let 'em know Marlo
+ step to any m-f-: Omar, Barksdale, whoever. My name IS my NAME." -- Marlo
+ Stansfield, The Wire (paraphrased)
+
+* Reputation from Past Actions:
+
+ bq. "The man you just killed was just released from prison. He could've f-in'
+ walked. All he had to do was say my dad's name, but he didn't; he kept his
+ f-ing mouth shut. And did his f-in' time, and he did it like a man. He did
+ four years for us. So, Mr. Orange, you're tellin' me this very good friend of
+ mine, who did four years for my father, who in four years never made a deal,
+ no matter what they dangled in front of him, you're telling me that now, that
+ now this man is free, and we're making good on our commitment to him, he's
+ just gonna decide, out of the f-ing blue, to rip us off?" -- Nice Guy Eddie,
+ Reservoir Dogs
+
+* Access control
+
+** Role
+ * http://en.wikipedia.org/wiki/Role-based_access_control
+ * "Role-Based Access Control FAQ":http://csrc.nist.gov/groups/SNS/rbac/faq.html
+ * "Role Based Access Control and Role Based Security":http://csrc.nist.gov/groups/SNS/rbac/ from the NIST Computer Security Division
+
+
+* Auditing & Recovery
+
+h2. Concept
+
+@ The below is a mixture of half-baked, foolish and incomplete. Just sos you know. @
+
+* Identity here will mean 'online presence' -- user account, basically.
+* Person will mean the remote endpoint -- whether that's a person or robot or
+ company. (Security papers call this "Subject" but that's awful).
+* It's easy to confuse 'person' and 'identity', so easy I probably have below.
+
+Why do you need to authenticate? For authorization. So traditionally, we think
+
+ person <- (ath'n token) <- identity <- (policy) <- actions
+
+That is, actions are attached to an identity by security policy, identity is
+attached to a person by their authentication token.
+
+The problem is that we cannot authenticate a /person/, only the token they
+present: password, ATM card+PIN number, etc.
+bq. "The Doors of Durin, Lord of Moria. Speak friend, and enter" -- Tolkien
+
+Anyone who presents that card+PIN, Elvish catchphrase, or voice print will be
+authenticated to act as the corresponding identity (account holder, friend of
+the elves, nerdy scientist), and we have no control over those tokens.
+
+ person <- (ath'n token) <- identity <- (az'n policy) <- actions
+ ^^^^ This step is wrong.
+
+The solution is to not care, or rather to reframe our goals.
+
+What we actually want is not to /control/ users' actions, but to /predict/ them.
+When Mr. Blonde helps Mr. White rob a jewelry store it's a security failure for
+the store but a success for the crime gang. When Mr. Orange (an undercover cop)
+shoots Mr. Blonde it's a security failure for the crime gang and a success for
+the police. We want to know how to use
+
+ ( identity, past actions ) => (trust, future actions)
+
+If you can predict someone is a vandal or troll, don't let them change pages, or
+only let them post to Ye Flaming Pitte of Flamage.
+
+We can to reasonable satisfaction authenticate a token: only grant that
+identity to visitors who bear that token. So this part is fine:
+
+ person (token)<- identity
+
+But we have no control over authentication token - identity correspondence.
+This part is broken:
+
+ person x (token)<- identity
+
+The only one who does have that control is the person behind that identity.
+They can reasonably guarantee
+
+ person ->(token)<- identity
+
+If that person is going to be in your community, they have an interest in their
+identity: they want to be known as someone who isn't a punk, or doesn't troll,
+or does troll and better than anyone, or won't rat you out to the cops. The
+actions of a person are moderated by their interest in maintaining their
+reputation:
+
+ past actions -> reputation ->person
+
+So give up authorization in favor of auditing and recoverability, and authorize
+based on reputation -- on the past behavior and vouchsafes offered by the
+identity,
+
+ reputation -> trust -> permissions ->actions
+
+They want to know that they have full control of their identity; among other
+things, privacy and an understanding that nobody can act without permission on
+their behalf. In fact, we can assure that only a token-holder can assume the
+corresponding identity:
+
+ person ->(token)<->identity ->(trust) actions ->reputation ->person
+
+poop
+
+ reputation ->trust
+
+
+So we need to
+* Understand and encourage how their security interests aligns with ours,
+* Understand how it doesn't, and be robust in the face of that; and
+* Recover gracefully if it goes wrong.
+
+Instead of
+
+ authorization -> user -> token -> identity
+
+we assign roles based on
+
+ authorization <- trust <- reputation <- identity <- token <- person
+
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Tradeoffs.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Tradeoffs.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Tradeoffs.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,126 @@
+Guides to best practices:
+* "The OWASP Guide to Building Secure Web Applications":http://www.owasp.org/index.php/Category:OWASP_Guide_Project
+* "Secure Programming for Linux and Unix HOWTO":http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/web-authentication.html
+* [[http://www.coresecuritypatterns.com/patterns.htm]]
+
+***************************************************************************
+h2. Session resetting
+
+Best practices recommend that you regenerate all session tokens (for us, the
+browser session ID and the remember_token cookie) on any privilege change (for
+us, logging in or logging out) -- see http://tinyurl.com/5vdvuq. This release
+properly regenerates remember_token cookies, but does *not* by default
+reset_session.
+
+Calling reset_session can interact with Form Authentication tokens (a *much*
+more important security feature). If a visitor logs in but has a form open in
+another tab, or uses the back button to pull one up from their history (perhaps
+the one that required them to log in), they will get the exceedingly unpleasant
+Request Forgery error. Imagine spending twenty minutes crafting a devastating
+critique of this week's Battlestar Galactica episode, finding you need to log in
+before posting -- but then getting a Request Forgery when you re-attempt the
+post. Frak! Thus, it's disabled by defauly.
+
+On the other hand, this does moderately reduce your defense-in-depth against a
+"Cross-Site Request Forgery":http://en.wikipedia.org/wiki/CSRF attack. To
+enable session_resetting, look for any
+ # reset session
+lines in the app/controllers/session_controller.rb and
+app/controllers/users_controller.rb and uncomment them.
+
+***************************************************************************
+h2. Site Key
+
+A Site key gives additional protection against a dictionary attack if your
+DB is ever compromised. With no site key, we store
+ DB_password = hash(user_password, DB_user_salt)
+If your database were to be compromised you'd be vulnerable to a dictionary
+attack on all your stupid users' passwords. With a site key, we store
+ DB_password = hash(user_password, DB_user_salt, Code_site_key)
+That means an attacker needs access to both your site's code *and* its
+database to mount an "offline dictionary attack.":http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/web-authentication.html
+
+It's probably of minor importance, but recommended by best practices: 'defense
+in depth'. Needless to say, if you upload this to github or the youtubes or
+otherwise place it in public view you'll kinda defeat the point. Your users'
+passwords are still secure, and the world won't end, but defense_in_depth -= 1.
+
+Please note: if you change this, all the passwords will be invalidated, so DO
+keep it someplace secure. Use the random value given or type in the lyrics to
+your favorite Jay-Z song or something; any moderately long, unpredictable text.
+
+***************************************************************************
+h2. Password stretching
+
+If someone were to capture your user accounts database, they could farm it out
+for brute-force or dictionary-attack password cracking. "Password Stretching"
+makes brute force (even with a compromised database and site key) attacks
+harder, and scales with Moore's law. Basically, you apply the password
+encryption process several times, meaning that each brute-force attempt takes
+that much longer. Hash your password ten times, and a brute-force attack takes
+ten times longer; hash 100,000 times and an attack takes 100,000 times longer.
+
+ bq. "To squeeze the most security out of a limited-entropy password or
+ passphrase, we can use two techniques [salting and stretching]... that are so
+ simple and obvious that they should be used in every password system. There
+ is really no excuse not to use them. ... Choose stretching factor so computing
+ K from (salt, passwd) takes 200-1000 ms. Store r with the user's password, and
+ increase it as computers get faster." -- http://tinyurl.com/37lb73
+ Practical Security (Ferguson & Scheier) p350
+
+Now, adding even a 0.2s delay to page requests isn't justifiable for most online
+applications, and storing r is unnecessary (at least on your first design
+iteration). But
+ On a 1G Slicehost already under moderate load:
+ irb(main):005:0> puts Time.now; (10**6).times{ secure_digest(Time.now, rand) }; puts Time.now
+ Fri May 16 08:26:16 +0000 2008
+ Fri May 16 08:30:58 +0000 2008
+ => 280s/1M ~= 0.000_3 ms / digest
+A modest 10 (the default here) foldings makes brute forcing, even given the site
+key and database, 10 times harder at a 3ms penalty. An app that otherwise
+serves 100 reqs/s is reduced to 78 signin reqs/s; an app that does 10reqs/s is
+reduced to 9.7 signin reqs/s
+
+* http://www.owasp.org/index.php/Hashing_Java
+* "An Illustrated Guide to Cryptographic Hashes":http://www.unixwiz.net/techtips/iguide-crypto-hashes.html
+
+The default of 10 is a reasonable compromise, but the security-paranoid and
+resource-rich may consider increasing REST_AUTH_DIGEST_STRETCHES to match the
+one-second best-practices value, while those with existing userbases (whose
+passwords would otherwise no longer work) should leave the value at one.
+
+***************************************************************************
+h2. Token regeneration
+
+The session and the remember_token should both be expired and regenerated
+every time we cross the logged out / logged in barrier by either password
+or cookie. ("To reduce the risk from session hijacking":http://www.owasp.org/index.php/Session_Management#Regeneration_of_Session_Tokens
+and brute force attacks, the HTTP server can seamlessly expire and
+regenerate tokens. This decreases the window of opportunity for a replay or
+brute force attack.) It does mean we set the cookie more often.
+
+ http://www.owasp.org/index.php/Session_Management#Regeneration_of_Session_Tokens
+ http://palisade.plynt.com/issues/2004Jul/safe-auth-practices/
+
+
+***************************************************************************
+h2. Field validation
+
+We restrict login names to only contain the characters
+<nowiki>A-Za-z0-9.-_@</nowiki> This allows (most) email addresses and is safe
+for urls, database expressions (the at sign, technically reserved in a url, will
+survive in most browsers). If you want to be more permissive:
+* "URL-legal characters":http://www.blooberry.com/indexdot/html/topics/urlencoding.htm are <nowiki>-_.!~*'()</nowiki>
+* "XML-legal characters":http://www.sklar.com/blog/archives/96-XML-vs.-Control-Characters.html are <nowiki>Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]</nowiki>
+* "Email-address legal characters":http://tools.ietf.org/html/rfc2822#section-3.4.1 are <nowiki>0-9a-zA-Z!#\$%\&\'\*\+_/=\?^\-`\{|\}~\.</nowiki> but see "this discussion of what is sane"http://www.regular-expressions.info/email.html (as opposed to legal)
+
+We restrict email addresses to match only those actually seen in the wild,
+invalidating some that are technically allowed (characters such as % and ! that
+date back to UUCP days. The line to allow all RFC-2822 emails is commented out,
+so feel free to enable it, or remove this validation. See "this discussion of
+what is sane"http://www.regular-expressions.info/email.html as opposed to what
+is legal. Also understand that this is just a cursory bogus-input check --
+there's no guarantee that this email matches an account or is even well-formed.
+
+If you change these validations you should change the RSpec tests as well.
+
Added: branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Trustification.txt
===================================================================
--- branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Trustification.txt (rev 0)
+++ branches/gsoc08-mpwa/vendor/plugins/restful_authentication/notes/Trustification.txt 2008-07-15 04:48:51 UTC (rev 38302)
@@ -0,0 +1,49 @@
+See also
+* "Trustlet Wiki":http://www.trustlet.org/wiki
+
+Potential Ingredients for a trust metric
+
+h2. Reputation
+
+* Web of trust
+* Reputation systems
+** Akismet, Viking, etc.
+
+* prove_as_human Completing a
+* validate_email
+
+ logged_in
+ akismet, etc.
+ session duration
+
+h2. Accountability
+
+Does the person tied to this identity stand to lose or gain anything based on this action?
+
+
+h2. Past history
+
+* past history
+** we can revisit past trust decisions based on revised trust estimates
+* recency of errors (reduce trust on an application exception)
+
+h2. Commitment
+
+* are_you_sure -- ask for con
+* willingness to pay a "hate task" (compute big hash) a la Zed Shaw
+* send_me_one_cent a micropayment
+** shows commitment
+** secondary validation from payment system
+** offsets rist
+
+h2. Identity Binding
+
+* Stale sessions
+ bq. "If your application allows users to be logged in for long periods of time
+ ensure that controls are in place to revalidate a user’s authorization to a
+ resource. For example, if Bob has the role of “Top Secret” at 1:00, and at
+ 2:00 while he is logged in his role is reduced to Secret he should not be able
+ to access “Top Secret” data any more." -- http://www.owasp.org/index.php/Guide_to_Authorization
+
+* how I authenticated: for instance, 'logged in by cookie' << 'logged in by password'
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20080714/179df046/attachment-0001.html
More information about the macports-changes
mailing list