authpwn_rails 0.3.0 → 0.4.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.
- data/.project +1 -1
- data/README.rdoc +2 -1
- data/Rakefile +1 -3
- data/VERSION +1 -1
- data/app/controllers/session_controller.rb +27 -1
- data/authpwn_rails.gemspec +15 -17
- data/lib/authpwn_rails.rb +2 -1
- data/lib/authpwn_rails/engine.rb +5 -3
- data/lib/authpwn_rails/facebook_extensions.rb +121 -0
- data/lib/authpwn_rails/generators/facebook_generator.rb +18 -0
- data/lib/authpwn_rails/generators/session_views_generator.rb +16 -0
- data/lib/authpwn_rails/generators/templates/facebook_token.rb +5 -4
- data/lib/authpwn_rails/generators/templates/session/home.html.erb +4 -0
- data/lib/authpwn_rails/generators/templates/session/welcome.html.erb +4 -0
- data/lib/authpwn_rails/generators/templates/user.rb +5 -4
- data/lib/authpwn_rails/generators/user_generator.rb +16 -0
- data/lib/authpwn_rails/session.rb +6 -0
- data/lib/authpwn_rails/user_model.rb +105 -0
- data/test/helpers/application_controller.rb +2 -0
- data/test/helpers/db_setup.rb +3 -2
- data/test/helpers/view_helpers.rb +2 -0
- data/test/session_controller_test.rb +35 -1
- data/test/test_helper.rb +1 -0
- metadata +17 -47
- data/app/models/facebook_token.rb +0 -47
- data/app/models/user.rb +0 -70
- data/lib/authpwn_rails/facebook_token.rb +0 -44
- data/lib/authpwn_rails/generators/facebook_migration_generator.rb +0 -17
- data/lib/authpwn_rails/generators/user_migration_generator.rb +0 -17
data/.project
CHANGED
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -11,9 +11,7 @@ begin
|
|
11
11
|
gem.homepage = "http://github.com/costan/mini_auth_rails"
|
12
12
|
gem.authors = ["Victor Costan"]
|
13
13
|
gem.add_runtime_dependency "fbgraph_rails", ">= 0.1.3"
|
14
|
-
gem.
|
15
|
-
gem.add_development_dependency "actionpack", ">= 3.0.0.rc"
|
16
|
-
gem.add_development_dependency "activesupport", ">= 3.0.0.rc"
|
14
|
+
gem.add_runtime_dependency "rails", ">= 3.0.0.rc"
|
17
15
|
gem.add_development_dependency "sqlite3-ruby", ">= 1.3.0"
|
18
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
17
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
@@ -2,9 +2,35 @@
|
|
2
2
|
class SessionController < ApplicationController
|
3
3
|
authenticates_using_session
|
4
4
|
|
5
|
+
# GET /session
|
6
|
+
def show
|
7
|
+
@user = current_user || User.new
|
8
|
+
if @user.new_record?
|
9
|
+
render :action => :welcome
|
10
|
+
else
|
11
|
+
render :action => :home
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# POST /session
|
16
|
+
def create
|
17
|
+
@user = User.new params[:user]
|
18
|
+
self.current_user =
|
19
|
+
User.find_by_email_and_password @user.email, @user.password
|
20
|
+
|
21
|
+
respond_to do |format|
|
22
|
+
if current_user
|
23
|
+
format.html { redirect_to session_url }
|
24
|
+
else
|
25
|
+
flash[:notice] = 'Invalid e-mail or password'
|
26
|
+
format.html { redirect_to session_url }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
5
31
|
# DELETE /session
|
6
32
|
def destroy
|
7
33
|
self.current_user = nil
|
8
|
-
redirect_to
|
34
|
+
redirect_to session_url
|
9
35
|
end
|
10
36
|
end
|
data/authpwn_rails.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{authpwn_rails}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Victor Costan"]
|
12
|
-
s.date = %q{2010-08-
|
12
|
+
s.date = %q{2010-08-03}
|
13
13
|
s.description = %q{Works with Facebook.}
|
14
14
|
s.email = %q{victor@costan.us}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,22 +26,24 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"app/controllers/session_controller.rb",
|
28
28
|
"app/helpers/session_helper.rb",
|
29
|
-
"app/models/facebook_token.rb",
|
30
|
-
"app/models/user.rb",
|
31
29
|
"authpwn_rails.gemspec",
|
32
30
|
"config/routes.rb",
|
33
31
|
"lib/authpwn_rails.rb",
|
34
32
|
"lib/authpwn_rails/engine.rb",
|
35
|
-
"lib/authpwn_rails/
|
36
|
-
"lib/authpwn_rails/generators/
|
33
|
+
"lib/authpwn_rails/facebook_extensions.rb",
|
34
|
+
"lib/authpwn_rails/generators/facebook_generator.rb",
|
35
|
+
"lib/authpwn_rails/generators/session_views_generator.rb",
|
37
36
|
"lib/authpwn_rails/generators/templates/001_create_users.rb",
|
38
37
|
"lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb",
|
39
38
|
"lib/authpwn_rails/generators/templates/facebook_token.rb",
|
40
39
|
"lib/authpwn_rails/generators/templates/facebook_tokens.yml",
|
40
|
+
"lib/authpwn_rails/generators/templates/session/home.html.erb",
|
41
|
+
"lib/authpwn_rails/generators/templates/session/welcome.html.erb",
|
41
42
|
"lib/authpwn_rails/generators/templates/user.rb",
|
42
43
|
"lib/authpwn_rails/generators/templates/users.yml",
|
43
|
-
"lib/authpwn_rails/generators/
|
44
|
+
"lib/authpwn_rails/generators/user_generator.rb",
|
44
45
|
"lib/authpwn_rails/session.rb",
|
46
|
+
"lib/authpwn_rails/user_model.rb",
|
45
47
|
"test/cookie_controller_test.rb",
|
46
48
|
"test/facebook_controller_test.rb",
|
47
49
|
"test/facebook_token_test.rb",
|
@@ -49,6 +51,7 @@ Gem::Specification.new do |s|
|
|
49
51
|
"test/helpers/db_setup.rb",
|
50
52
|
"test/helpers/fbgraph.rb",
|
51
53
|
"test/helpers/routes.rb",
|
54
|
+
"test/helpers/view_helpers.rb",
|
52
55
|
"test/session_controller_test.rb",
|
53
56
|
"test/test_helper.rb",
|
54
57
|
"test/user_test.rb"
|
@@ -68,7 +71,8 @@ Gem::Specification.new do |s|
|
|
68
71
|
"test/helpers/application_controller.rb",
|
69
72
|
"test/helpers/routes.rb",
|
70
73
|
"test/helpers/fbgraph.rb",
|
71
|
-
"test/helpers/db_setup.rb"
|
74
|
+
"test/helpers/db_setup.rb",
|
75
|
+
"test/helpers/view_helpers.rb"
|
72
76
|
]
|
73
77
|
|
74
78
|
if s.respond_to? :specification_version then
|
@@ -77,22 +81,16 @@ Gem::Specification.new do |s|
|
|
77
81
|
|
78
82
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
79
83
|
s.add_runtime_dependency(%q<fbgraph_rails>, [">= 0.1.3"])
|
80
|
-
s.
|
81
|
-
s.add_development_dependency(%q<actionpack>, [">= 3.0.0.rc"])
|
82
|
-
s.add_development_dependency(%q<activesupport>, [">= 3.0.0.rc"])
|
84
|
+
s.add_runtime_dependency(%q<rails>, [">= 3.0.0.rc"])
|
83
85
|
s.add_development_dependency(%q<sqlite3-ruby>, [">= 1.3.0"])
|
84
86
|
else
|
85
87
|
s.add_dependency(%q<fbgraph_rails>, [">= 0.1.3"])
|
86
|
-
s.add_dependency(%q<
|
87
|
-
s.add_dependency(%q<actionpack>, [">= 3.0.0.rc"])
|
88
|
-
s.add_dependency(%q<activesupport>, [">= 3.0.0.rc"])
|
88
|
+
s.add_dependency(%q<rails>, [">= 3.0.0.rc"])
|
89
89
|
s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.0"])
|
90
90
|
end
|
91
91
|
else
|
92
92
|
s.add_dependency(%q<fbgraph_rails>, [">= 0.1.3"])
|
93
|
-
s.add_dependency(%q<
|
94
|
-
s.add_dependency(%q<actionpack>, [">= 3.0.0.rc"])
|
95
|
-
s.add_dependency(%q<activesupport>, [">= 3.0.0.rc"])
|
93
|
+
s.add_dependency(%q<rails>, [">= 3.0.0.rc"])
|
96
94
|
s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.0"])
|
97
95
|
end
|
98
96
|
end
|
data/lib/authpwn_rails.rb
CHANGED
data/lib/authpwn_rails/engine.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'authpwn_rails'
|
2
2
|
require 'rails'
|
3
3
|
|
4
4
|
# :nodoc: namespace
|
@@ -17,8 +17,10 @@ class Engine < Rails::Engine
|
|
17
17
|
# paths.config.locales = "config/locales"
|
18
18
|
paths.config.routes = "config/routes.rb"
|
19
19
|
|
20
|
-
|
21
|
-
require '
|
20
|
+
generators do
|
21
|
+
require 'authpwn_rails/generators/facebook_generator.rb'
|
22
|
+
require 'authpwn_rails/generators/session_views_generator.rb'
|
23
|
+
require 'authpwn_rails/generators/user_generator.rb'
|
22
24
|
end
|
23
25
|
end # class AuthpwnRails::Engine
|
24
26
|
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
# :nodoc: namespace
|
5
|
+
module AuthpwnRails
|
6
|
+
|
7
|
+
# :nodoc: namespace
|
8
|
+
module FacebookExtensions
|
9
|
+
|
10
|
+
|
11
|
+
# Mixed into ActiveController::Base
|
12
|
+
module ControllerMixin
|
13
|
+
def self.included(base)
|
14
|
+
base.send :extend, ControllerClassMethods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# Methods here become ActiveController::Base class methods.
|
20
|
+
module ControllerClassMethods
|
21
|
+
# Authenticates users via Facebook OAuth2, using fbgraph_rails.
|
22
|
+
#
|
23
|
+
# The User model class must implement for_facebook_token. The controller
|
24
|
+
# should obtain the Facebook token, using probes_facebook_access_token or
|
25
|
+
# requires_facebook_access_token.
|
26
|
+
def authenticates_using_facebook(options = {})
|
27
|
+
include ControllerInstanceMethods
|
28
|
+
before_filter :authenticate_using_facebook_access_token, options
|
29
|
+
end
|
30
|
+
end # module AuthpwnRails::FacebookExtensions::ControllerClassMethods
|
31
|
+
|
32
|
+
|
33
|
+
# Included in controllers that call authenticates_using_facebook.
|
34
|
+
module ControllerInstanceMethods
|
35
|
+
def authenticate_using_facebook_access_token
|
36
|
+
return true if current_user
|
37
|
+
if access_token = current_facebook_access_token
|
38
|
+
self.current_user = User.for_facebook_token access_token
|
39
|
+
# NOTE: nixing the token from the session so the user won't be logged on
|
40
|
+
# immediately after logging off
|
41
|
+
self.current_facebook_access_token = nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
private :authenticate_using_facebook_access_token
|
45
|
+
end # module AuthpwnRails::FacebookExtensions::ControllerInstanceMethods
|
46
|
+
|
47
|
+
ActionController::Base.send :include, ControllerMixin
|
48
|
+
|
49
|
+
|
50
|
+
# Mixed into ActiveRecord::Base
|
51
|
+
module ModelMixin
|
52
|
+
def self.included(base)
|
53
|
+
base.send :extend, ModelClassMethods
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Methods here become ActiveRecord::Base class methods.
|
59
|
+
module ModelClassMethods
|
60
|
+
# Extends the model with all that it needs to be PwnAuth's user model.
|
61
|
+
def pwnauth_facebook_token_model
|
62
|
+
# The user whose token this is.
|
63
|
+
belongs_to :user
|
64
|
+
validates :user, :presence => true
|
65
|
+
|
66
|
+
# A unique ID on the Facebook site for the user owning this token.
|
67
|
+
validates :external_uid, :length => 1..32, :presence => true
|
68
|
+
|
69
|
+
# The OAuth2 access token.
|
70
|
+
validates :access_token, :length => 1..128, :presence => true
|
71
|
+
|
72
|
+
extend ModelMetaclassMethods
|
73
|
+
include ModelInstanceMethods
|
74
|
+
end
|
75
|
+
end # module AuthpwnRails::UserModel::ModelClassMethods
|
76
|
+
|
77
|
+
|
78
|
+
# Included in the metaclass of models that call pwnauth_facebook_token_model.
|
79
|
+
module ModelMetaclassMethods
|
80
|
+
# Finds or creates the model containing a token.
|
81
|
+
#
|
82
|
+
# If a model for the same user exists, the model is updated with the given
|
83
|
+
# token. Otherwise, a new model will be created, together with a user.
|
84
|
+
def for(access_token)
|
85
|
+
uid = uid_from_token access_token
|
86
|
+
token = self.where(:external_uid => uid).first
|
87
|
+
if token
|
88
|
+
token.access_token = access_token
|
89
|
+
else
|
90
|
+
token = FacebookToken.new :external_uid => uid,
|
91
|
+
:access_token => access_token
|
92
|
+
token.user = User.create_with_facebook_token token
|
93
|
+
end
|
94
|
+
token.save!
|
95
|
+
token
|
96
|
+
end
|
97
|
+
|
98
|
+
# Extracts the Facebook user ID from a OAuth2 token.
|
99
|
+
#
|
100
|
+
# This is a hack. It works based on the current format, but might break at any
|
101
|
+
# time. Hopefully, we'll eventually have an official way of pulling the UID
|
102
|
+
# out of an OAuth2 token.
|
103
|
+
def uid_from_token(access_token)
|
104
|
+
access_token.split('|')[1].split('-').last
|
105
|
+
end
|
106
|
+
end # module AuthpwnRails::UserModel::ModelMetaclassMethods
|
107
|
+
|
108
|
+
|
109
|
+
# Included in models that call pwnauth_user_model.
|
110
|
+
module ModelInstanceMethods
|
111
|
+
# FBGraph client loaded with this access token.
|
112
|
+
def facebook_client
|
113
|
+
@client ||= FBGraphRails.fbclient(access_token)
|
114
|
+
end
|
115
|
+
end # module AuthpwnRails::UserModel::ModelInstanceMethods
|
116
|
+
|
117
|
+
ActiveRecord::Base.send :include, ModelMixin
|
118
|
+
|
119
|
+
end # namespace AuthpwnRails::FacebookExtensions
|
120
|
+
|
121
|
+
end # namespace AuthpwnRails
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module AuthpwnRails
|
3
|
+
|
4
|
+
|
5
|
+
class FacebookGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def create_facebook_model
|
9
|
+
copy_file 'facebook_token.rb',
|
10
|
+
File.join('app', 'models', 'facebook_token.rb')
|
11
|
+
copy_file '002_create_facebook_tokens.rb',
|
12
|
+
File.join('db', 'migrations', '20100725000002_create_facebook_tokens.rb')
|
13
|
+
copy_file 'facebook_tokens.yml',
|
14
|
+
File.join('test', 'fixtures', 'facebook_tokens.yml')
|
15
|
+
end
|
16
|
+
end # class AuthpwnRails::FacebookGenerator
|
17
|
+
|
18
|
+
end # namespace AuthpwnRails
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module AuthpwnRails
|
3
|
+
|
4
|
+
|
5
|
+
class SessionViewsGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def create_session_views
|
9
|
+
copy_file File.join('session', 'home.html.erb'),
|
10
|
+
File.join('app', 'views', 'session', 'home.html.erb')
|
11
|
+
copy_file File.join('session', 'welcome.html.erb'),
|
12
|
+
File.join('app', 'views', 'session', 'welcome.html.erb')
|
13
|
+
end
|
14
|
+
end # class AuthpwnRails::SessionViewsGenerator
|
15
|
+
|
16
|
+
end # namespace AuthpwnRails
|
@@ -1,5 +1,6 @@
|
|
1
|
-
#
|
2
|
-
class FacebookToken
|
3
|
-
|
4
|
-
|
1
|
+
# Wraps an OAuth2 access token for Facebook.
|
2
|
+
class FacebookToken < ActiveRecord::Base
|
3
|
+
pwnauth_facebook_token_model
|
4
|
+
|
5
|
+
# Add your extensions to the FacebookToken class here.
|
5
6
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module AuthpwnRails
|
3
|
+
|
4
|
+
|
5
|
+
class UserGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def create_user_model
|
9
|
+
copy_file 'user.rb', File.join('app', 'models', 'user.rb')
|
10
|
+
copy_file '001_create_users.rb',
|
11
|
+
File.join('db', 'migrations', '20100725000001_create_users.rb')
|
12
|
+
copy_file 'users.yml', File.join('test', 'fixtures', 'users.yml')
|
13
|
+
end
|
14
|
+
end # class AuthpwnRails::UserGenerator
|
15
|
+
|
16
|
+
end # namespace AuthpwnRails
|
@@ -56,6 +56,12 @@ class ActionController::TestCase
|
|
56
56
|
def set_session_current_user(user)
|
57
57
|
request.session[:current_user_id] = user ? user.id : nil
|
58
58
|
end
|
59
|
+
|
60
|
+
# The authenticated user in the test session.
|
61
|
+
def session_current_user
|
62
|
+
return nil unless user_id = request.session[:current_user_id]
|
63
|
+
User.find user_id
|
64
|
+
end
|
59
65
|
end
|
60
66
|
|
61
67
|
end # namespace AuthpwnRails::Session
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
# :nodoc: namespace
|
4
|
+
module AuthpwnRails
|
5
|
+
|
6
|
+
# :nodoc: namespace
|
7
|
+
module UserModel
|
8
|
+
|
9
|
+
|
10
|
+
# Mixed into ActiveRecord::Base
|
11
|
+
module ModelMixin
|
12
|
+
def self.included(base)
|
13
|
+
base.send :extend, ModelClassMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# Methods here become ActiveRecord::Base class methods.
|
19
|
+
module ModelClassMethods
|
20
|
+
# Extends the model with all that it needs to be PwnAuth's user model.
|
21
|
+
def pwnauth_user_model
|
22
|
+
# E-mail address identifying the user account.
|
23
|
+
validates :email, :format => /^[A-Za-z0-9.+_]+@[^@]*\.(\w+)$/,
|
24
|
+
:presence => true, :length => 1..64, :uniqueness => true
|
25
|
+
|
26
|
+
# Random string preventing dictionary attacks on the password database.
|
27
|
+
validates :password_salt, :length => 1..16, :allow_nil => true
|
28
|
+
|
29
|
+
# SHA-256 of (salt + password).
|
30
|
+
validates :password_hash, :length => 1..64, :allow_nil => true
|
31
|
+
|
32
|
+
# Virtual attribute: the user's password.
|
33
|
+
attr_reader :password
|
34
|
+
validates :password, :confirmation => true
|
35
|
+
|
36
|
+
# Virtual attribute: confirmation for the user's password.
|
37
|
+
attr_accessor :password_confirmation
|
38
|
+
validates_confirmation_of :password
|
39
|
+
|
40
|
+
extend ModelMetaclassMethods
|
41
|
+
include ModelInstanceMethods
|
42
|
+
end
|
43
|
+
end # module AuthpwnRails::UserModel::ModelClassMethods
|
44
|
+
|
45
|
+
|
46
|
+
# Included in the metaclass of models that call pwnauth_user_model.
|
47
|
+
module ModelMetaclassMethods
|
48
|
+
# The authenticated user or nil.
|
49
|
+
def find_by_email_and_password(email, password)
|
50
|
+
@user = where(:email => email).first
|
51
|
+
(@user && @user.password_matches?(password)) ? @user : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Computes a password hash from a raw password and a salt.
|
55
|
+
def hash_password(password, salt)
|
56
|
+
Digest::SHA2.hexdigest(password + salt)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generates a random salt value.
|
60
|
+
def random_salt
|
61
|
+
(0...16).map { |i| 1 + rand(255) }.pack('C*')
|
62
|
+
end
|
63
|
+
|
64
|
+
# Fills out a new user's information based on a Facebook access token.
|
65
|
+
def create_with_facebook_token(token)
|
66
|
+
self.create :email => "#{token.external_uid}@graph.facebook.com"
|
67
|
+
end
|
68
|
+
|
69
|
+
# The user that owns a given Facebook OAuth2 token.
|
70
|
+
#
|
71
|
+
# A new user will be created if the token doesn't belong to any user. This is
|
72
|
+
# the case for a new visitor.
|
73
|
+
def for_facebook_token(access_token)
|
74
|
+
FacebookToken.for(access_token).user
|
75
|
+
end
|
76
|
+
end # module AuthpwnRails::UserModel::ModelMetaclassMethods
|
77
|
+
|
78
|
+
|
79
|
+
# Included in models that call pwnauth_user_model.
|
80
|
+
module ModelInstanceMethods
|
81
|
+
# Resets the virtual password attributes.
|
82
|
+
def reset_password
|
83
|
+
@password = @password_confirmation = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Compares the given password against the user's stored password.
|
87
|
+
#
|
88
|
+
# Returns +true+ for a match, +false+ otherwise.
|
89
|
+
def password_matches?(passwd)
|
90
|
+
password_hash == self.class.hash_password(passwd, password_salt)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Password virtual attribute.
|
94
|
+
def password=(new_password)
|
95
|
+
@password = new_password
|
96
|
+
self.password_salt = self.class.random_salt
|
97
|
+
self.password_hash = self.class.hash_password new_password, password_salt
|
98
|
+
end
|
99
|
+
end # module AuthpwnRails::UserModel::ModelInstanceMethods
|
100
|
+
|
101
|
+
ActiveRecord::Base.send :include, ModelMixin
|
102
|
+
|
103
|
+
end # namespace AuthpwnRails::UserModel
|
104
|
+
|
105
|
+
end # namespace AuthpwnRails
|
data/test/helpers/db_setup.rb
CHANGED
@@ -8,8 +8,8 @@ CreateUsers.up
|
|
8
8
|
require 'authpwn_rails/generators/templates/002_create_facebook_tokens.rb'
|
9
9
|
CreateFacebookTokens.up
|
10
10
|
|
11
|
-
require
|
12
|
-
require
|
11
|
+
require 'authpwn_rails/generators/templates/facebook_token.rb'
|
12
|
+
require 'authpwn_rails/generators/templates/user.rb'
|
13
13
|
|
14
14
|
# :nodoc: open TestCase to setup fixtures
|
15
15
|
class ActiveSupport::TestCase
|
@@ -18,6 +18,7 @@ class ActiveSupport::TestCase
|
|
18
18
|
self.fixture_path =
|
19
19
|
File.expand_path '../../../lib/authpwn_rails/generators/templates',
|
20
20
|
__FILE__
|
21
|
+
|
21
22
|
self.use_transactional_fixtures = false
|
22
23
|
self.use_instantiated_fixtures = false
|
23
24
|
self.pre_loaded_fixtures = false
|
@@ -6,12 +6,46 @@ class SessionControllerTest < ActionController::TestCase
|
|
6
6
|
setup do
|
7
7
|
@user = users(:john)
|
8
8
|
end
|
9
|
+
|
10
|
+
test "show renders welcome without a user" do
|
11
|
+
get :show
|
12
|
+
assert_template :welcome
|
13
|
+
assert_nil assigns(:current_user)
|
14
|
+
end
|
15
|
+
|
16
|
+
test "show renders home with a user" do
|
17
|
+
set_session_current_user @user
|
18
|
+
get :show
|
19
|
+
assert_template :home
|
20
|
+
assert_equal @user, assigns(:current_user)
|
21
|
+
end
|
22
|
+
|
23
|
+
test "create logs in with good account details" do
|
24
|
+
post :create, :user => { :email => @user.email, :password => 'password' }
|
25
|
+
assert_redirected_to session_url
|
26
|
+
assert_equal @user, assigns(:current_user), 'instance variable'
|
27
|
+
assert_equal @user, session_current_user, 'session'
|
28
|
+
end
|
29
|
+
|
30
|
+
test "create does not log in with bad password" do
|
31
|
+
post :create, :user => { :email => @user.email, :password => 'fail' }
|
32
|
+
assert_redirected_to session_url
|
33
|
+
assert_nil assigns(:current_user), 'instance variable'
|
34
|
+
assert_nil session_current_user, 'session'
|
35
|
+
end
|
36
|
+
|
37
|
+
test "create does not log in with bad e-mail" do
|
38
|
+
post :create, :user => { :email => 'nobody@gmail.com', :password => 'no' }
|
39
|
+
assert_redirected_to session_url
|
40
|
+
assert_nil assigns(:current_user), 'instance variable'
|
41
|
+
assert_nil session_current_user, 'session'
|
42
|
+
end
|
9
43
|
|
10
44
|
test "logout" do
|
11
45
|
set_session_current_user @user
|
12
46
|
delete :destroy
|
13
47
|
|
14
|
-
assert_redirected_to
|
48
|
+
assert_redirected_to session_url
|
15
49
|
assert_nil assigns(:current_user)
|
16
50
|
end
|
17
51
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authpwn_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 4
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Victor Costan
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-08-
|
18
|
+
date: 2010-08-03 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
38
|
+
name: rails
|
39
39
|
prerelease: false
|
40
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
@@ -49,46 +49,12 @@ dependencies:
|
|
49
49
|
- 0
|
50
50
|
- rc
|
51
51
|
version: 3.0.0.rc
|
52
|
-
type: :
|
52
|
+
type: :runtime
|
53
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
54
|
- !ruby/object:Gem::Dependency
|
89
55
|
name: sqlite3-ruby
|
90
56
|
prerelease: false
|
91
|
-
requirement: &
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
92
58
|
none: false
|
93
59
|
requirements:
|
94
60
|
- - ">="
|
@@ -100,7 +66,7 @@ dependencies:
|
|
100
66
|
- 0
|
101
67
|
version: 1.3.0
|
102
68
|
type: :development
|
103
|
-
version_requirements: *
|
69
|
+
version_requirements: *id003
|
104
70
|
description: Works with Facebook.
|
105
71
|
email: victor@costan.us
|
106
72
|
executables: []
|
@@ -120,22 +86,24 @@ files:
|
|
120
86
|
- VERSION
|
121
87
|
- app/controllers/session_controller.rb
|
122
88
|
- app/helpers/session_helper.rb
|
123
|
-
- app/models/facebook_token.rb
|
124
|
-
- app/models/user.rb
|
125
89
|
- authpwn_rails.gemspec
|
126
90
|
- config/routes.rb
|
127
91
|
- lib/authpwn_rails.rb
|
128
92
|
- lib/authpwn_rails/engine.rb
|
129
|
-
- lib/authpwn_rails/
|
130
|
-
- lib/authpwn_rails/generators/
|
93
|
+
- lib/authpwn_rails/facebook_extensions.rb
|
94
|
+
- lib/authpwn_rails/generators/facebook_generator.rb
|
95
|
+
- lib/authpwn_rails/generators/session_views_generator.rb
|
131
96
|
- lib/authpwn_rails/generators/templates/001_create_users.rb
|
132
97
|
- lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb
|
133
98
|
- lib/authpwn_rails/generators/templates/facebook_token.rb
|
134
99
|
- lib/authpwn_rails/generators/templates/facebook_tokens.yml
|
100
|
+
- lib/authpwn_rails/generators/templates/session/home.html.erb
|
101
|
+
- lib/authpwn_rails/generators/templates/session/welcome.html.erb
|
135
102
|
- lib/authpwn_rails/generators/templates/user.rb
|
136
103
|
- lib/authpwn_rails/generators/templates/users.yml
|
137
|
-
- lib/authpwn_rails/generators/
|
104
|
+
- lib/authpwn_rails/generators/user_generator.rb
|
138
105
|
- lib/authpwn_rails/session.rb
|
106
|
+
- lib/authpwn_rails/user_model.rb
|
139
107
|
- test/cookie_controller_test.rb
|
140
108
|
- test/facebook_controller_test.rb
|
141
109
|
- test/facebook_token_test.rb
|
@@ -143,6 +111,7 @@ files:
|
|
143
111
|
- test/helpers/db_setup.rb
|
144
112
|
- test/helpers/fbgraph.rb
|
145
113
|
- test/helpers/routes.rb
|
114
|
+
- test/helpers/view_helpers.rb
|
146
115
|
- test/session_controller_test.rb
|
147
116
|
- test/test_helper.rb
|
148
117
|
- test/user_test.rb
|
@@ -191,3 +160,4 @@ test_files:
|
|
191
160
|
- test/helpers/routes.rb
|
192
161
|
- test/helpers/fbgraph.rb
|
193
162
|
- test/helpers/db_setup.rb
|
163
|
+
- test/helpers/view_helpers.rb
|
@@ -1,47 +0,0 @@
|
|
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
|
data/app/models/user.rb
DELETED
@@ -1,70 +0,0 @@
|
|
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
|
@@ -1,44 +0,0 @@
|
|
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
|
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,17 +0,0 @@
|
|
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
|