authpwn_rails 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. data/.document +5 -0
  2. data/.gitignore +24 -0
  3. data/.project +17 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +21 -0
  6. data/Rakefile +57 -0
  7. data/VERSION +1 -0
  8. data/app/controllers/session_controller.rb +10 -0
  9. data/app/helpers/session_helper.rb +4 -0
  10. data/app/models/facebook_token.rb +47 -0
  11. data/app/models/user.rb +70 -0
  12. data/authpwn_rails.gemspec +99 -0
  13. data/config/routes.rb +3 -0
  14. data/lib/authpwn_rails/engine.rb +25 -0
  15. data/lib/authpwn_rails/facebook_token.rb +44 -0
  16. data/lib/authpwn_rails/generators/facebook_migration_generator.rb +17 -0
  17. data/lib/authpwn_rails/generators/templates/001_create_users.rb +17 -0
  18. data/lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb +15 -0
  19. data/lib/authpwn_rails/generators/templates/facebook_token.rb +5 -0
  20. data/lib/authpwn_rails/generators/templates/facebook_tokens.yml +10 -0
  21. data/lib/authpwn_rails/generators/templates/user.rb +5 -0
  22. data/lib/authpwn_rails/generators/templates/users.yml +9 -0
  23. data/lib/authpwn_rails/generators/user_migration_generator.rb +17 -0
  24. data/lib/authpwn_rails/session.rb +63 -0
  25. data/lib/authpwn_rails.rb +15 -0
  26. data/test/cookie_controller_test.rb +41 -0
  27. data/test/facebook_controller_test.rb +43 -0
  28. data/test/facebook_token_test.rb +28 -0
  29. data/test/helpers/application_controller.rb +3 -0
  30. data/test/helpers/db_setup.rb +25 -0
  31. data/test/helpers/fbgraph.rb +6 -0
  32. data/test/helpers/routes.rb +15 -0
  33. data/test/session_controller_test.rb +17 -0
  34. data/test/test_helper.rb +17 -0
  35. data/test/user_test.rb +88 -0
  36. metadata +193 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## ECLIPSE
9
+ .loadpath
10
+
11
+ ## EMACS
12
+ *~
13
+ \#*
14
+ .\#*
15
+
16
+ ## VIM
17
+ *.swp
18
+
19
+ ## PROJECT::GENERAL
20
+ coverage
21
+ rdoc
22
+ pkg
23
+
24
+ ## PROJECT::SPECIFIC
data/.project ADDED
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>pwnauth_rails</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.rubypeople.rdt.core.rubybuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>org.rubypeople.rdt.core.rubynature</nature>
16
+ </natures>
17
+ </projectDescription>
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Victor Costan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = mini_auth_rails
2
+
3
+ User authentication for a Ruby on Rails 3 application. Works with Facebook.
4
+
5
+ == Integration
6
+
7
+ TBD
8
+
9
+ == Note on Patches/Pull Requests
10
+
11
+ * Fork the project.
12
+ * Make your feature addition or bug fix.
13
+ * Add tests for it. This is important so I don't break it in a
14
+ future version unintentionally.
15
+ * Commit, do not mess with rakefile, version, or history.
16
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
17
+ * Send me a pull request. Bonus points for topic branches.
18
+
19
+ == Copyright
20
+
21
+ Copyright (c) 2010 Victor Costan, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "authpwn_rails"
8
+ gem.summary = %Q{User authentication for Rails 3 applications.}
9
+ gem.description = %Q{Works with Facebook.}
10
+ gem.email = "victor@costan.us"
11
+ gem.homepage = "http://github.com/costan/mini_auth_rails"
12
+ gem.authors = ["Victor Costan"]
13
+ gem.add_runtime_dependency "fbgraph_rails", ">= 0.1.3"
14
+ gem.add_development_dependency "activerecord", ">= 3.0.0.rc"
15
+ gem.add_development_dependency "actionpack", ">= 3.0.0.rc"
16
+ gem.add_development_dependency "activesupport", ">= 3.0.0.rc"
17
+ gem.add_development_dependency "sqlite3-ruby", ">= 1.3.0"
18
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'rake/testtask'
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/*_test.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new do |test|
35
+ test.libs << 'test'
36
+ test.pattern = 'test/**/*_test.rb'
37
+ test.verbose = true
38
+ end
39
+ rescue LoadError
40
+ task :rcov do
41
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
42
+ end
43
+ end
44
+
45
+ task :test => :check_dependencies
46
+
47
+ task :default => :test
48
+
49
+ require 'rake/rdoctask'
50
+ Rake::RDocTask.new do |rdoc|
51
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "authpwn_rails #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
@@ -0,0 +1,10 @@
1
+ # Manages logging in and out of the application.
2
+ class SessionController < ApplicationController
3
+ authenticates_using_session
4
+
5
+ # DELETE /session
6
+ def destroy
7
+ self.current_user = nil
8
+ redirect_to root_url
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module SessionHelper
2
+ # The user currently logged in. Nil if no user is logged in.
3
+ attr_reader :current_user
4
+ end
@@ -0,0 +1,47 @@
1
+ require 'active_record'
2
+
3
+
4
+ # Wraps an OAuth2 access token for Facebook.
5
+ class FacebookToken < ActiveRecord::Base
6
+ # The user whose token this is.
7
+ belongs_to :user
8
+ validates :user, :presence => true
9
+
10
+ # A unique ID on the Facebook site for the user owning this token.
11
+ validates :external_uid, :length => 1..32, :presence => true
12
+
13
+ # The OAuth2 access token.
14
+ validates :access_token, :length => 1..128, :presence => true
15
+
16
+ # FBGraph client loaded with this access token.
17
+ def facebook_client
18
+ @client ||= FBGraphRails.fbclient(access_token)
19
+ end
20
+
21
+ # Finds or creates the model containing a token.
22
+ #
23
+ # If a model for the same user exists, the model is updated with the given
24
+ # token. Otherwise, a new model will be created, together with a user.
25
+ def self.for(access_token)
26
+ uid = uid_from_token access_token
27
+ token = self.where(:external_uid => uid).first
28
+ if token
29
+ token.access_token = access_token
30
+ else
31
+ token = FacebookToken.new :external_uid => uid,
32
+ :access_token => access_token
33
+ token.user = User.create_with_facebook_token token
34
+ end
35
+ token.save!
36
+ token
37
+ end
38
+
39
+ # Extracts the Facebook user ID from a OAuth2 token.
40
+ #
41
+ # This is a hack. It works based on the current format, but might break at any
42
+ # time. Hopefully, we'll eventually have an official way of pulling the UID
43
+ # out of an OAuth2 token.
44
+ def self.uid_from_token(access_token)
45
+ access_token.split('|')[1].split('-').last
46
+ end
47
+ end
@@ -0,0 +1,70 @@
1
+ require 'active_record'
2
+
3
+
4
+ # An user account.
5
+ class User < ActiveRecord::Base
6
+ # E-mail address identifying the user account.
7
+ validates :email, :format => /^[A-Za-z0-9.+_]+@[^@]*\.(\w+)$/,
8
+ :presence => true, :length => 1..64, :uniqueness => true
9
+
10
+ # Random string preventing dictionary attacks on the password database.
11
+ validates :password_salt, :length => 1..16, :allow_nil => true
12
+
13
+ # SHA-256 of (salt + password).
14
+ validates :password_hash, :length => 1..64, :allow_nil => true
15
+
16
+ # Virtual attribute: the user's password.
17
+ attr_reader :password
18
+ validates :password, :confirmation => true
19
+ def password=(new_password)
20
+ @password = new_password
21
+ self.password_salt = self.class.random_salt
22
+ self.password_hash = self.class.hash_password new_password, password_salt
23
+ end
24
+
25
+ # Virtual attribute: confirmation for the user's password.
26
+ attr_accessor :password_confirmation
27
+ validates_confirmation_of :password
28
+
29
+ # The authenticated user or nil.
30
+ def self.find_by_email_and_password(email, password)
31
+ @user = User.where(:email => email).first
32
+ (@user && @user.password_matches?(password)) ? @user : nil
33
+ end
34
+
35
+ # Compares the given password against the user's stored password.
36
+ #
37
+ # Returns +true+ for a match, +false+ otherwise.
38
+ def password_matches?(passwd)
39
+ password_hash == User.hash_password(passwd, password_salt)
40
+ end
41
+
42
+ # Computes a password hash from a raw password and a salt.
43
+ def self.hash_password(password, salt)
44
+ Digest::SHA2.hexdigest(password + salt)
45
+ end
46
+
47
+ # Generates a random salt value.
48
+ def self.random_salt
49
+ (0...16).map { |i| 1 + rand(255) }.pack('C*')
50
+ end
51
+
52
+ # Resets the virtual password attributes.
53
+ def reset_password
54
+ @password = @password_confirmation = nil
55
+ end
56
+
57
+
58
+ # Fills out a new user's information based on a Facebook access token.
59
+ def self.create_with_facebook_token(token)
60
+ self.create :email => "#{token.external_uid}@graph.facebook.com"
61
+ end
62
+
63
+ # The user that owns a given Facebook OAuth2 token.
64
+ #
65
+ # A new user will be created if the token doesn't belong to any user. This is
66
+ # the case for a new visitor.
67
+ def self.for_facebook_token(access_token)
68
+ FacebookToken.for(access_token).user
69
+ end
70
+ end
@@ -0,0 +1,99 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{authpwn_rails}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Victor Costan"]
12
+ s.date = %q{2010-08-02}
13
+ s.description = %q{Works with Facebook.}
14
+ s.email = %q{victor@costan.us}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".project",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "app/controllers/session_controller.rb",
28
+ "app/helpers/session_helper.rb",
29
+ "app/models/facebook_token.rb",
30
+ "app/models/user.rb",
31
+ "authpwn_rails.gemspec",
32
+ "config/routes.rb",
33
+ "lib/authpwn_rails.rb",
34
+ "lib/authpwn_rails/engine.rb",
35
+ "lib/authpwn_rails/facebook_token.rb",
36
+ "lib/authpwn_rails/generators/facebook_migration_generator.rb",
37
+ "lib/authpwn_rails/generators/templates/001_create_users.rb",
38
+ "lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb",
39
+ "lib/authpwn_rails/generators/templates/facebook_token.rb",
40
+ "lib/authpwn_rails/generators/templates/facebook_tokens.yml",
41
+ "lib/authpwn_rails/generators/templates/user.rb",
42
+ "lib/authpwn_rails/generators/templates/users.yml",
43
+ "lib/authpwn_rails/generators/user_migration_generator.rb",
44
+ "lib/authpwn_rails/session.rb",
45
+ "test/cookie_controller_test.rb",
46
+ "test/facebook_controller_test.rb",
47
+ "test/facebook_token_test.rb",
48
+ "test/helpers/application_controller.rb",
49
+ "test/helpers/db_setup.rb",
50
+ "test/helpers/fbgraph.rb",
51
+ "test/helpers/routes.rb",
52
+ "test/session_controller_test.rb",
53
+ "test/test_helper.rb",
54
+ "test/user_test.rb"
55
+ ]
56
+ s.homepage = %q{http://github.com/costan/mini_auth_rails}
57
+ s.rdoc_options = ["--charset=UTF-8"]
58
+ s.require_paths = ["lib"]
59
+ s.rubygems_version = %q{1.3.7}
60
+ s.summary = %q{User authentication for Rails 3 applications.}
61
+ s.test_files = [
62
+ "test/facebook_token_test.rb",
63
+ "test/user_test.rb",
64
+ "test/cookie_controller_test.rb",
65
+ "test/test_helper.rb",
66
+ "test/facebook_controller_test.rb",
67
+ "test/session_controller_test.rb",
68
+ "test/helpers/application_controller.rb",
69
+ "test/helpers/routes.rb",
70
+ "test/helpers/fbgraph.rb",
71
+ "test/helpers/db_setup.rb"
72
+ ]
73
+
74
+ if s.respond_to? :specification_version then
75
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
76
+ s.specification_version = 3
77
+
78
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
79
+ s.add_runtime_dependency(%q<fbgraph_rails>, [">= 0.1.3"])
80
+ s.add_development_dependency(%q<activerecord>, [">= 3.0.0.rc"])
81
+ s.add_development_dependency(%q<actionpack>, [">= 3.0.0.rc"])
82
+ s.add_development_dependency(%q<activesupport>, [">= 3.0.0.rc"])
83
+ s.add_development_dependency(%q<sqlite3-ruby>, [">= 1.3.0"])
84
+ else
85
+ s.add_dependency(%q<fbgraph_rails>, [">= 0.1.3"])
86
+ s.add_dependency(%q<activerecord>, [">= 3.0.0.rc"])
87
+ s.add_dependency(%q<actionpack>, [">= 3.0.0.rc"])
88
+ s.add_dependency(%q<activesupport>, [">= 3.0.0.rc"])
89
+ s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.0"])
90
+ end
91
+ else
92
+ s.add_dependency(%q<fbgraph_rails>, [">= 0.1.3"])
93
+ s.add_dependency(%q<activerecord>, [">= 3.0.0.rc"])
94
+ s.add_dependency(%q<actionpack>, [">= 3.0.0.rc"])
95
+ s.add_dependency(%q<activesupport>, [">= 3.0.0.rc"])
96
+ s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.0"])
97
+ end
98
+ end
99
+
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails::Application.routes.draw do |map|
2
+ resource :session, :controller => 'session'
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'mini_auth_rails'
2
+ require 'rails'
3
+
4
+ # :nodoc: namespace
5
+ module AuthpwnRails
6
+
7
+ class Engine < Rails::Engine
8
+ paths.app = "app"
9
+ paths.app.controllers = "app/controllers"
10
+ paths.app.helpers = "app/helpers"
11
+ paths.app.models = "app/models"
12
+ paths.app.views = "app/views"
13
+ # paths.lib = "lib"
14
+ # paths.lib.tasks = "lib/tasks"
15
+ # paths.config = "config"
16
+ # paths.config.initializers = "config/initializers"
17
+ # paths.config.locales = "config/locales"
18
+ paths.config.routes = "config/routes.rb"
19
+
20
+ def generators
21
+ require 'mini_auth_rails/generators/user_model_generator.rb'
22
+ end
23
+ end # class AuthpwnRails::Engine
24
+
25
+ end # namespace AuthpwnRails
@@ -0,0 +1,44 @@
1
+ require 'action_controller'
2
+
3
+ # :nodoc: namespace
4
+ module AuthpwnRails
5
+
6
+ # :nodoc: namespace
7
+ module FacebookToken
8
+
9
+ # Mixed into ActiveController::Base
10
+ module ControllerMixin
11
+ def self.included(base)
12
+ base.send :extend, ControllerClassMethods
13
+ end
14
+ end
15
+
16
+ # Methods here become ActiveController::Base class methods.
17
+ module ControllerClassMethods
18
+ # Authenticates users via Facebook OAuth2, using fbgraph_rails.
19
+ #
20
+ # The User model class must implement for_facebook_token. The controller
21
+ # should obtain the Facebook token, using probes_facebook_access_token or
22
+ # requires_facebook_access_token.
23
+ def authenticates_using_facebook(options = {})
24
+ include ControllerInstanceMethods
25
+ before_filter :authenticate_using_facebook_access_token, options
26
+ end
27
+ end
28
+
29
+ # Included in controllers that call authenticates_using_facebook.
30
+ module ControllerInstanceMethods
31
+ def authenticate_using_facebook_access_token
32
+ return true if current_user
33
+ if access_token = current_facebook_access_token
34
+ self.current_user = User.for_facebook_token access_token
35
+ end
36
+ end
37
+ private :authenticate_using_facebook_access_token
38
+ end
39
+
40
+ ActionController::Base.send :include, ControllerMixin
41
+
42
+ end # namespace AuthpwnRails::FacebookToken
43
+
44
+ end # namespace AuthpwnRails
@@ -0,0 +1,17 @@
1
+ # :nodoc: namespace
2
+ module AuthpwnRails
3
+
4
+
5
+ class FacebookMigrationGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def create_session_model
9
+ template 'facebook_token.rb',
10
+ File.join('app/models', class_path, 'facebook_token.rb')
11
+ template '002_create_facebook_tokens.rb',
12
+ File.join('db/migrations', '20100725000002_create_facebook_tokens.rb')
13
+ template 'facebook_tokens.yml', File.join('test/fixtures', 'facebook_tokens.yml')
14
+ end
15
+ end # class AuthpwnRails::UserMigrationGenerator
16
+
17
+ end # namespace AuthpwnRails
@@ -0,0 +1,17 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :email, :limit => 64, :null => false
5
+ t.string :password_salt, :limit => 16, :null => true
6
+ t.string :password_hash, :limit => 64, :null => true
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :users, :email, :unique => true, :null => false
12
+ end
13
+
14
+ def self.down
15
+ drop_table :users
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ class CreateFacebookTokens < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :facebook_tokens do |t|
4
+ t.integer :user_id, :null => false
5
+ t.string :external_uid, :limit => 32, :null => false
6
+ t.string :access_token, :limit => 128, :null => false
7
+ end
8
+
9
+ add_index :facebook_tokens, :external_uid, :unique => true, :null => false
10
+ end
11
+
12
+ def self.down
13
+ drop_table :facebook_tokens
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ # :nodoc: extensions
2
+ class FacebookToken
3
+ # Add your extensions to the FacebookToken class here.
4
+
5
+ end
@@ -0,0 +1,10 @@
1
+ # Test account vic.tor@costan.us
2
+ jane:
3
+ user: jane
4
+ external_uid: 100001181310542
5
+ access_token: 125502267478972|d2ecea6d763d2fb17cfa70fa-100001181310542|h849k0nQBq4FkAVEGVgeyoSd_RA.
6
+
7
+ john:
8
+ user: john
9
+ external_uid: 702659
10
+ access_token: 702659|ffffffffffffffffffffffff-702659|ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
@@ -0,0 +1,5 @@
1
+ # :nodoc: extensions
2
+ class User
3
+ # Add your extensions to the User class here.
4
+
5
+ end
@@ -0,0 +1,9 @@
1
+ jane:
2
+ email: jane@gmail.com
3
+ password_salt: 5678
4
+ password_hash: <%= User.hash_password('pa55w0rd', '5678').inspect %>
5
+
6
+ john:
7
+ email: john@gmail.com
8
+ password_salt: 1234
9
+ password_hash: <%= User.hash_password('password', '1234').inspect %>
@@ -0,0 +1,17 @@
1
+ # :nodoc: namespace
2
+ module AuthpwnRails
3
+
4
+
5
+ class UserMigrationGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def create_session_model
9
+ template 'user_token.rb',
10
+ File.join('app/models', class_path, 'user.rb')
11
+ template '001_create_users.rb',
12
+ File.join('db/migrations', '20100725000001_create_users.rb')
13
+ template 'users.yml', File.join('test/fixtures', 'users.yml')
14
+ end
15
+ end # class AuthpwnRails::UserMigrationGenerator
16
+
17
+ end # namespace AuthpwnRails
@@ -0,0 +1,63 @@
1
+ require 'action_controller'
2
+
3
+ # :nodoc: namespace
4
+ module AuthpwnRails
5
+
6
+ # :nodoc: namespace
7
+ module Session
8
+
9
+ # Mixed into ActiveController::Base
10
+ module ControllerMixin
11
+ def self.included(base)
12
+ base.send :extend, ControllerClassMethods
13
+ end
14
+ end
15
+
16
+ # Methods here become ActiveController::Base class methods.
17
+ module ControllerClassMethods
18
+ # Keeps track of the currently authenticated user via the session.
19
+ #
20
+ # Assumes the existence of a User model. A bare ActiveModel model will do the
21
+ # trick. Model instances must implement id, and the model class must implement
22
+ # find_by_id.
23
+ def authenticates_using_session(options = {})
24
+ include ControllerInstanceMethods
25
+ before_filter :authenticate_using_session, options
26
+ end
27
+ end
28
+
29
+ # Included in controllers that call authenticates_using_session.
30
+ module ControllerInstanceMethods
31
+ attr_reader :current_user
32
+
33
+ def current_user=(user)
34
+ @current_user = user
35
+ if user
36
+ session[:current_user_id] = user.id
37
+ else
38
+ session.delete :current_user_id
39
+ end
40
+ end
41
+
42
+ def authenticate_using_session
43
+ return true if current_user
44
+ user_id = session[:current_user_id]
45
+ user = user_id && User.find_by_id(user_id)
46
+ self.current_user = user if user
47
+ end
48
+ private :authenticate_using_session
49
+ end
50
+
51
+ ActionController::Base.send :include, ControllerMixin
52
+
53
+ # :nodoc: add session modification
54
+ class ActionController::TestCase
55
+ # Sets the authenticated user in the test session.
56
+ def set_session_current_user(user)
57
+ request.session[:current_user_id] = user ? user.id : nil
58
+ end
59
+ end
60
+
61
+ end # namespace AuthpwnRails::Session
62
+
63
+ end # namespace AuthpwnRails
@@ -0,0 +1,15 @@
1
+ # :nodoc: namespace
2
+ module AuthpwnRails
3
+ end
4
+
5
+ require 'authpwn_rails/facebook_token.rb'
6
+ require 'authpwn_rails/session.rb'
7
+
8
+ if defined?(Rails)
9
+ require 'authpwn_rails/engine.rb'
10
+
11
+ # HACK(costan): this works around a known Rails bug
12
+ # https://rails.lighthouseapp.com/projects/8994/tickets/1905-apphelpers-within-plugin-not-being-mixed-in
13
+ require File.expand_path('../../app/helpers/session_helper.rb', __FILE__)
14
+ ActionController::Base.helper SessionHelper
15
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ # Mock controller used for testing session handling.
4
+ class CookieController < ApplicationController
5
+ authenticates_using_session
6
+
7
+ def show
8
+ if current_user
9
+ render :text => "User: #{current_user.id}"
10
+ else
11
+ render :text => "No user"
12
+ end
13
+ end
14
+ end
15
+
16
+ class CookieControllerTest < ActionController::TestCase
17
+ setup do
18
+ @user = users(:john)
19
+ end
20
+
21
+ test "no user_id in session" do
22
+ get :show
23
+ assert_response :success
24
+ assert_nil assigns(:current_user)
25
+ assert_equal 'No user', response.body
26
+ end
27
+
28
+ test "valid user_id in session" do
29
+ set_session_current_user @user
30
+ get :show
31
+ assert_response :success
32
+ assert_equal @user, assigns(:current_user)
33
+ assert_equal "User: #{Fixtures.identify(:john)}", response.body
34
+ end
35
+
36
+ test "invalid user_id in session" do
37
+ get :show, {}, :current_user_id => 999
38
+ assert_response :success
39
+ assert_nil assigns(:current_user)
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ # Mock controller used for testing session handling.
4
+ class FacebookController < ApplicationController
5
+ authenticates_using_session
6
+ probes_facebook_access_token
7
+ authenticates_using_facebook
8
+
9
+ def show
10
+ if current_user
11
+ render :text => "User: #{current_user.id}"
12
+ else
13
+ render :text => "No user"
14
+ end
15
+ end
16
+ end
17
+
18
+ class FacebookControllerTest < ActionController::TestCase
19
+ setup do
20
+ @user = users(:john)
21
+ @new_token = 'facebook:new_token|boom'
22
+ end
23
+
24
+ test "no facebook token" do
25
+ get :show
26
+ assert_response :success
27
+ assert_nil assigns(:current_user)
28
+ end
29
+
30
+ test "facebook token for existing user" do
31
+ set_session_current_facebook_token facebook_tokens(:john).access_token
32
+ get :show, {}
33
+ assert_response :success
34
+ assert_equal @user, assigns(:current_user)
35
+ end
36
+
37
+ test "new facebook token" do
38
+ set_session_current_facebook_token @new_token
39
+ get :show, {}
40
+ assert_response :success
41
+ assert !(@user == assigns(:current_user))
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ class FacebookTokenTest < ActiveSupport::TestCase
4
+ setup do
5
+ @code = '125502267478972|057806abb79e632e0f7dde62-100001181310542|y5SoPVcXoEl214vfs--F3y-Z0Xk.'
6
+ end
7
+
8
+ test "uid_from_token" do
9
+ assert_equal '100001181310542', FacebookToken.uid_from_token(@code)
10
+ end
11
+
12
+ test "for with existing access token" do
13
+ assert_equal facebook_tokens(:jane), FacebookToken.for(@code),
14
+ 'Wrong token'
15
+ assert_equal @code, facebook_tokens(:jane).reload.access_token,
16
+ 'Token not refreshed'
17
+ end
18
+
19
+ test "for with new access token" do
20
+ token = nil
21
+ assert_difference 'FacebookToken.count', 1 do
22
+ token = FacebookToken.for @code.gsub('100001181310542', '3141592')
23
+ end
24
+ assert_equal '3141592@graph.facebook.com', token.user.email
25
+ assert !token.new_record?, 'New token not saved'
26
+ assert !token.user.new_record?, "New token's user not saved"
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ # :nodoc: stubbed, because controllers inherit from it
2
+ class ApplicationController < ActionController::Base
3
+ end
@@ -0,0 +1,25 @@
1
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3',
2
+ :database => ':memory:'
3
+ ActiveRecord::Base.configurations = true
4
+
5
+ ActiveRecord::Migration.verbose = false
6
+ require 'authpwn_rails/generators/templates/001_create_users.rb'
7
+ CreateUsers.up
8
+ require 'authpwn_rails/generators/templates/002_create_facebook_tokens.rb'
9
+ CreateFacebookTokens.up
10
+
11
+ require File.expand_path('../../../app/models/facebook_token.rb', __FILE__)
12
+ require File.expand_path('../../../app/models/user.rb', __FILE__)
13
+
14
+ # :nodoc: open TestCase to setup fixtures
15
+ class ActiveSupport::TestCase
16
+ include ActiveRecord::TestFixtures
17
+
18
+ self.fixture_path =
19
+ File.expand_path '../../../lib/authpwn_rails/generators/templates',
20
+ __FILE__
21
+ self.use_transactional_fixtures = false
22
+ self.use_instantiated_fixtures = false
23
+ self.pre_loaded_fixtures = false
24
+ fixtures :all
25
+ end
@@ -0,0 +1,6 @@
1
+ # :nodoc: stub FBGraphRails.config because it depends on Rails.root
2
+ module FBGraphRails
3
+ def self.config
4
+ { 'id' => '12345', 'secret' => 'awesome', 'scope' => []}
5
+ end
6
+ end
@@ -0,0 +1,15 @@
1
+ # :nodoc: the routes used in all tests
2
+ class ActionController::TestCase
3
+ def setup_routes
4
+ @routes = ActionController::Routing::RouteSet.new
5
+ @routes.draw do
6
+ resource :cookie, :controller => 'cookie'
7
+ resource :facebook, :controller => 'facebook'
8
+ resource :session, :controller => 'session'
9
+ root :to => 'session#index'
10
+ end
11
+ ApplicationController.send :include, @routes.url_helpers
12
+ end
13
+
14
+ setup :setup_routes
15
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ require File.expand_path('../../app/controllers/session_controller', __FILE__)
4
+
5
+ class SessionControllerTest < ActionController::TestCase
6
+ setup do
7
+ @user = users(:john)
8
+ end
9
+
10
+ test "logout" do
11
+ set_session_current_user @user
12
+ delete :destroy
13
+
14
+ assert_redirected_to root_url
15
+ assert_nil assigns(:current_user)
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ require 'action_pack'
5
+ require 'active_record'
6
+ require 'active_support'
7
+
8
+ require 'fbgraph_rails'
9
+ require 'fbgraph_rails/controller'
10
+ require 'sqlite3'
11
+
12
+ require 'authpwn_rails'
13
+
14
+ require 'helpers/application_controller.rb'
15
+ require 'helpers/db_setup.rb'
16
+ require 'helpers/fbgraph.rb'
17
+ require 'helpers/routes.rb'
data/test/user_test.rb ADDED
@@ -0,0 +1,88 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ class UserTest < ActiveSupport::TestCase
4
+ def setup
5
+ @user = User.new :password => 'awesome',
6
+ :password_confirmation => 'awesome',
7
+ :email => 'dvdjohn@mit.edu'
8
+ end
9
+
10
+ test 'password_salt not required' do
11
+ @user.password_salt = nil
12
+ assert @user.valid?
13
+ end
14
+
15
+ test 'password_salt length' do
16
+ @user.password_salt = '12345' * 4
17
+ assert !@user.valid?, 'Long salt'
18
+ @user.password_salt = ''
19
+ assert !@user.valid?, 'Empty salt'
20
+ end
21
+
22
+ test 'password_hash not required' do
23
+ @user.password_hash = nil
24
+ assert @user.valid?
25
+ end
26
+
27
+ test 'password_hash length' do
28
+ @user.password_hash = '12345' * 13
29
+ assert !@user.valid?, 'Long hash'
30
+ @user.password_hash = ''
31
+ assert !@user.valid?, 'Empty hash'
32
+ end
33
+
34
+ test 'email presence' do
35
+ @user.email = nil
36
+ assert !@user.valid?
37
+ end
38
+
39
+ test 'email length' do
40
+ @user.email = 'abcde' * 12 + '@mit.edu'
41
+ assert !@user.valid?, 'Overly long user name'
42
+ end
43
+
44
+ test 'email format' do
45
+ ['cos tan@gmail.com', 'costan@x@mit.edu'].each do |email|
46
+ @user.email = email
47
+ assert !@user.valid?, "Bad email format - #{name}"
48
+ end
49
+ end
50
+
51
+ test 'email uniqueness' do
52
+ @user.email = users(:john).email
53
+ assert !@user.valid?
54
+ end
55
+
56
+ test 'password not required' do
57
+ @user.reset_password
58
+ assert @user.valid?
59
+ end
60
+
61
+ test 'password confirmation' do
62
+ @user.password_confirmation = 'not awesome'
63
+ assert !@user.valid?
64
+ end
65
+
66
+ test 'password_matches?' do
67
+ assert_equal true, @user.password_matches?('awesome')
68
+ assert_equal false, @user.password_matches?('not awesome'), 'Bogus password'
69
+ assert_equal false, @user.password_matches?('password'),
70
+ "Another user's password"
71
+ end
72
+
73
+ test 'find_by_email_and_password' do
74
+ assert_equal users(:john),
75
+ User.find_by_email_and_password('john@gmail.com', 'password')
76
+ assert_equal nil,
77
+ User.find_by_email_and_password('john@gmail.com', 'pa55w0rd'),
78
+ "Jane's password on John's account"
79
+ assert_equal users(:jane),
80
+ User.find_by_email_and_password('jane@gmail.com', 'pa55w0rd')
81
+ assert_equal nil,
82
+ User.find_by_email_and_password('jane@gmail.com', 'password'),
83
+ "John's password on Jane's account"
84
+ assert_equal nil,
85
+ User.find_by_email_and_password('john@gmail.com', 'awesome'),
86
+ 'Bogus password'
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authpwn_rails
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
+ platform: ruby
12
+ authors:
13
+ - Victor Costan
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-02 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: fbgraph_rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 29
30
+ segments:
31
+ - 0
32
+ - 1
33
+ - 3
34
+ version: 0.1.3
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: activerecord
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 7712042
46
+ segments:
47
+ - 3
48
+ - 0
49
+ - 0
50
+ - rc
51
+ version: 3.0.0.rc
52
+ type: :development
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: actionpack
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 7712042
63
+ segments:
64
+ - 3
65
+ - 0
66
+ - 0
67
+ - rc
68
+ version: 3.0.0.rc
69
+ type: :development
70
+ version_requirements: *id003
71
+ - !ruby/object:Gem::Dependency
72
+ name: activesupport
73
+ prerelease: false
74
+ requirement: &id004 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 7712042
80
+ segments:
81
+ - 3
82
+ - 0
83
+ - 0
84
+ - rc
85
+ version: 3.0.0.rc
86
+ type: :development
87
+ version_requirements: *id004
88
+ - !ruby/object:Gem::Dependency
89
+ name: sqlite3-ruby
90
+ prerelease: false
91
+ requirement: &id005 !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 27
97
+ segments:
98
+ - 1
99
+ - 3
100
+ - 0
101
+ version: 1.3.0
102
+ type: :development
103
+ version_requirements: *id005
104
+ description: Works with Facebook.
105
+ email: victor@costan.us
106
+ executables: []
107
+
108
+ extensions: []
109
+
110
+ extra_rdoc_files:
111
+ - LICENSE
112
+ - README.rdoc
113
+ files:
114
+ - .document
115
+ - .gitignore
116
+ - .project
117
+ - LICENSE
118
+ - README.rdoc
119
+ - Rakefile
120
+ - VERSION
121
+ - app/controllers/session_controller.rb
122
+ - app/helpers/session_helper.rb
123
+ - app/models/facebook_token.rb
124
+ - app/models/user.rb
125
+ - authpwn_rails.gemspec
126
+ - config/routes.rb
127
+ - lib/authpwn_rails.rb
128
+ - lib/authpwn_rails/engine.rb
129
+ - lib/authpwn_rails/facebook_token.rb
130
+ - lib/authpwn_rails/generators/facebook_migration_generator.rb
131
+ - lib/authpwn_rails/generators/templates/001_create_users.rb
132
+ - lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb
133
+ - lib/authpwn_rails/generators/templates/facebook_token.rb
134
+ - lib/authpwn_rails/generators/templates/facebook_tokens.yml
135
+ - lib/authpwn_rails/generators/templates/user.rb
136
+ - lib/authpwn_rails/generators/templates/users.yml
137
+ - lib/authpwn_rails/generators/user_migration_generator.rb
138
+ - lib/authpwn_rails/session.rb
139
+ - test/cookie_controller_test.rb
140
+ - test/facebook_controller_test.rb
141
+ - test/facebook_token_test.rb
142
+ - test/helpers/application_controller.rb
143
+ - test/helpers/db_setup.rb
144
+ - test/helpers/fbgraph.rb
145
+ - test/helpers/routes.rb
146
+ - test/session_controller_test.rb
147
+ - test/test_helper.rb
148
+ - test/user_test.rb
149
+ has_rdoc: true
150
+ homepage: http://github.com/costan/mini_auth_rails
151
+ licenses: []
152
+
153
+ post_install_message:
154
+ rdoc_options:
155
+ - --charset=UTF-8
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 3
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ none: false
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ hash: 3
173
+ segments:
174
+ - 0
175
+ version: "0"
176
+ requirements: []
177
+
178
+ rubyforge_project:
179
+ rubygems_version: 1.3.7
180
+ signing_key:
181
+ specification_version: 3
182
+ summary: User authentication for Rails 3 applications.
183
+ test_files:
184
+ - test/facebook_token_test.rb
185
+ - test/user_test.rb
186
+ - test/cookie_controller_test.rb
187
+ - test/test_helper.rb
188
+ - test/facebook_controller_test.rb
189
+ - test/session_controller_test.rb
190
+ - test/helpers/application_controller.rb
191
+ - test/helpers/routes.rb
192
+ - test/helpers/fbgraph.rb
193
+ - test/helpers/db_setup.rb