authpwn_rails 0.9.6 → 0.10.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/.travis.yml +6 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +38 -36
- data/README.rdoc +6 -11
- data/VERSION +1 -1
- data/authpwn_rails.gemspec +30 -22
- data/lib/authpwn_rails.rb +2 -2
- data/lib/authpwn_rails/credential_model.rb +38 -0
- data/lib/authpwn_rails/credentials.rb +10 -0
- data/lib/authpwn_rails/credentials/email.rb +30 -0
- data/lib/authpwn_rails/credentials/facebook.rb +77 -0
- data/lib/authpwn_rails/credentials/password.rb +63 -0
- data/lib/authpwn_rails/engine.rb +5 -7
- data/lib/authpwn_rails/facebook_session.rb +5 -5
- data/lib/authpwn_rails/generators/{session_generator.rb → all_generator.rb} +28 -9
- data/lib/authpwn_rails/generators/templates/001_create_users.rb +3 -11
- data/lib/authpwn_rails/generators/templates/002_create_credentials.rb +19 -0
- data/lib/authpwn_rails/generators/templates/credential.rb +16 -0
- data/lib/authpwn_rails/generators/templates/credentials.yml +34 -0
- data/lib/authpwn_rails/generators/templates/session/forbidden.html.erb +2 -2
- data/lib/authpwn_rails/generators/templates/session/home.html.erb +1 -1
- data/lib/authpwn_rails/generators/templates/session/new.html.erb +6 -6
- data/lib/authpwn_rails/generators/templates/session_controller.rb +1 -1
- data/lib/authpwn_rails/generators/templates/session_controller_test.rb +2 -2
- data/lib/authpwn_rails/generators/templates/user.rb +2 -2
- data/lib/authpwn_rails/generators/templates/users.yml +5 -8
- data/lib/authpwn_rails/session.rb +7 -7
- data/lib/authpwn_rails/session_controller.rb +15 -13
- data/lib/authpwn_rails/test_extensions.rb +6 -6
- data/lib/authpwn_rails/user_model.rb +23 -92
- data/test/email_credential_test.rb +50 -0
- data/test/facebook_controller_test.rb +7 -2
- data/test/facebook_credential_test.rb +74 -0
- data/test/helpers/db_setup.rb +4 -4
- data/test/helpers/fbgraph.rb +6 -2
- data/test/password_credential_test.rb +67 -0
- data/test/session_controller_api_test.rb +12 -12
- data/test/test_helper.rb +1 -0
- data/test/user_test.rb +11 -100
- metadata +41 -25
- data/lib/authpwn_rails/facebook_token_model.rb +0 -66
- data/lib/authpwn_rails/generators/facebook_generator.rb +0 -18
- data/lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb +0 -15
- data/lib/authpwn_rails/generators/templates/facebook_token.rb +0 -6
- data/lib/authpwn_rails/generators/templates/facebook_tokens.yml +0 -10
- data/lib/authpwn_rails/generators/users_generator.rb +0 -16
- data/test/facebook_token_test.rb +0 -28
@@ -0,0 +1,63 @@
|
|
1
|
+
# :namespace
|
2
|
+
module Credentials
|
3
|
+
|
4
|
+
# Associates a password with the user account.
|
5
|
+
class Password < ::Credential
|
6
|
+
# Virtual attribute: the user's password.
|
7
|
+
attr_accessor :password
|
8
|
+
validates :password, :confirmation => true, :presence => true
|
9
|
+
|
10
|
+
# Virtual attribute: confirmation for the user's password.
|
11
|
+
attr_accessor :password_confirmation
|
12
|
+
|
13
|
+
# A user can have a single password
|
14
|
+
validates :user_id, :uniqueness => true
|
15
|
+
|
16
|
+
# Compares the given password against the user's stored password.
|
17
|
+
#
|
18
|
+
# Returns +true+ for a match, +false+ otherwise.
|
19
|
+
def authenticate(password)
|
20
|
+
return false unless key
|
21
|
+
key == self.class.hash_password(password, key.split('|', 2).first)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Password virtual attribute.
|
25
|
+
def password=(new_password)
|
26
|
+
@password = new_password
|
27
|
+
salt = self.class.random_salt
|
28
|
+
self.key = new_password && self.class.hash_password(new_password, salt)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Resets the virtual password attributes.
|
32
|
+
def clear_plaintext
|
33
|
+
@password = @password_confirmation = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# The authenticated user or nil.
|
37
|
+
def self.authenticate_email(email, password)
|
38
|
+
email_cred = Credentials::Email.where(:name => email).
|
39
|
+
includes(:user => :credentials).first
|
40
|
+
return nil unless email_cred
|
41
|
+
credential = email_cred.user.credentials.
|
42
|
+
find { |c| c.kind_of? Credentials::Password }
|
43
|
+
credential.authenticate(password) ? email_cred.user : nil
|
44
|
+
end
|
45
|
+
|
46
|
+
# Computes a password hash from a raw password and a salt.
|
47
|
+
def self.hash_password(password, salt)
|
48
|
+
salt + '|' + Digest::SHA2.hexdigest(password + salt)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Generates a random salt value.
|
52
|
+
def self.random_salt
|
53
|
+
[(0...12).map { |i| 1 + rand(255) }.pack('C*')].pack('m').strip
|
54
|
+
end
|
55
|
+
end # class Credentials::Password
|
56
|
+
|
57
|
+
end # namespace Credentials
|
58
|
+
|
59
|
+
module Authpwn::UserModel::InstanceMethods
|
60
|
+
def password_credential
|
61
|
+
credentials.find { |c| c.instance_of?(Credentials::Password) }
|
62
|
+
end
|
63
|
+
end
|
data/lib/authpwn_rails/engine.rb
CHANGED
@@ -2,13 +2,11 @@ require 'authpwn_rails'
|
|
2
2
|
require 'rails'
|
3
3
|
|
4
4
|
# :nodoc: namespace
|
5
|
-
module
|
5
|
+
module Authpwn
|
6
6
|
|
7
7
|
class Engine < Rails::Engine
|
8
8
|
generators do
|
9
|
-
require 'authpwn_rails/generators/
|
10
|
-
require 'authpwn_rails/generators/session_generator.rb'
|
11
|
-
require 'authpwn_rails/generators/users_generator.rb'
|
9
|
+
require 'authpwn_rails/generators/all_generator.rb'
|
12
10
|
end
|
13
11
|
|
14
12
|
initializer 'authpwn.rspec.extensions' do
|
@@ -16,12 +14,12 @@ class Engine < Rails::Engine
|
|
16
14
|
require 'rspec'
|
17
15
|
|
18
16
|
RSpec.configure do |c|
|
19
|
-
c.include
|
17
|
+
c.include Authpwn::TestExtensions
|
20
18
|
end
|
21
19
|
rescue LoadError
|
22
20
|
# No RSpec, no extensions.
|
23
21
|
end
|
24
22
|
end
|
25
|
-
end # class
|
23
|
+
end # class Authpwn::Engine
|
26
24
|
|
27
|
-
end # namespace
|
25
|
+
end # namespace Authpwn
|
@@ -8,13 +8,13 @@ class ActionController::Base
|
|
8
8
|
# should obtain the Facebook token, using probes_facebook_access_token or
|
9
9
|
# requires_facebook_access_token.
|
10
10
|
def self.authenticates_using_facebook(options = {})
|
11
|
-
include
|
11
|
+
include Authpwn::FacebookControllerInstanceMethods
|
12
12
|
before_filter :authenticate_using_facebook_access_token, options
|
13
13
|
end
|
14
|
-
end # module
|
14
|
+
end # module Authpwn::FacebookExtensions::ControllerClassMethods
|
15
15
|
|
16
16
|
# :nodoc: namespace
|
17
|
-
module
|
17
|
+
module Authpwn
|
18
18
|
|
19
19
|
# Included in controllers that call authenticates_using_facebook.
|
20
20
|
module FacebookControllerInstanceMethods
|
@@ -28,6 +28,6 @@ module FacebookControllerInstanceMethods
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
private :authenticate_using_facebook_access_token
|
31
|
-
end # module
|
31
|
+
end # module Authpwn::FacebookControllerInstanceMethods
|
32
32
|
|
33
|
-
end # namespace
|
33
|
+
end # namespace Authpwn
|
@@ -1,13 +1,36 @@
|
|
1
1
|
# :nodoc: namespace
|
2
2
|
module Authpwn
|
3
3
|
|
4
|
-
# rails g authpwn:
|
5
|
-
class
|
4
|
+
# rails g authpwn:all
|
5
|
+
class AllGenerator < Rails::Generators::Base
|
6
6
|
source_root File.expand_path("../templates", __FILE__)
|
7
7
|
|
8
|
-
def
|
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', 'migrate', '20100725000001_create_users.rb')
|
12
|
+
copy_file 'users.yml', File.join('test', 'fixtures', 'users.yml')
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_credential_model
|
16
|
+
copy_file 'credential.rb', File.join('app', 'models', 'credential.rb')
|
17
|
+
copy_file '002_create_credentials.rb',
|
18
|
+
File.join('db', 'migrate', '20100725000002_create_credentials.rb')
|
19
|
+
copy_file 'credentials.yml',
|
20
|
+
File.join('test', 'fixtures', 'credentials.yml')
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_session_controller
|
9
24
|
copy_file 'session_controller.rb',
|
10
25
|
File.join('app', 'controllers', 'session_controller.rb')
|
26
|
+
copy_file File.join('session_controller_test.rb'),
|
27
|
+
File.join('test', 'functional', 'session_controller_test.rb')
|
28
|
+
|
29
|
+
route "resource :session, :controller => 'session'"
|
30
|
+
route "root :to => 'session#show'"
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_session_views
|
11
34
|
copy_file File.join('session', 'forbidden.html.erb'),
|
12
35
|
File.join('app', 'views', 'session', 'forbidden.html.erb')
|
13
36
|
copy_file File.join('session', 'home.html.erb'),
|
@@ -16,11 +39,7 @@ class SessionGenerator < Rails::Generators::Base
|
|
16
39
|
File.join('app', 'views', 'session', 'new.html.erb')
|
17
40
|
copy_file File.join('session', 'welcome.html.erb'),
|
18
41
|
File.join('app', 'views', 'session', 'welcome.html.erb')
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
route "resource :session, :controller => 'session'"
|
23
|
-
end
|
24
|
-
end # class Authpwn::SessionViewsGenerator
|
42
|
+
end
|
43
|
+
end # class Authpwn::AllGenerator
|
25
44
|
|
26
45
|
end # namespace Authpwn
|
@@ -1,19 +1,11 @@
|
|
1
1
|
class CreateUsers < ActiveRecord::Migration
|
2
|
-
def
|
2
|
+
def change
|
3
3
|
create_table :users do |t|
|
4
|
-
t.string :
|
5
|
-
t.string :email_hash, :limit => 64, :null => false
|
6
|
-
t.string :password_salt, :limit => 16, :null => true
|
7
|
-
t.string :password_hash, :limit => 64, :null => true
|
4
|
+
t.string :exuid, :limit => 32, :null => false
|
8
5
|
|
9
6
|
t.timestamps
|
10
7
|
end
|
11
8
|
|
12
|
-
add_index :users, :
|
13
|
-
add_index :users, :email_hash, :unique => true, :null => false
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.down
|
17
|
-
drop_table :users
|
9
|
+
add_index :users, :exuid, :unique => true, :null => false
|
18
10
|
end
|
19
11
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateCredentials < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :credentials do |t|
|
4
|
+
t.references :user, :null => false
|
5
|
+
t.string :type, :limit => 32, :null => false
|
6
|
+
t.string :name, :limit => 256, :null => true
|
7
|
+
|
8
|
+
t.boolean :verified, :null => false, :default => false
|
9
|
+
|
10
|
+
t.binary :key, :limit => 2.kilobytes, :null => true
|
11
|
+
end
|
12
|
+
|
13
|
+
# All the credentials (maybe of a specific type) belonging to a user.
|
14
|
+
add_index :credentials, [:user_id, :type], :unique => false,
|
15
|
+
:null => false
|
16
|
+
# A specific credential, to find out what user it belongs to.
|
17
|
+
add_index :credentials, [:type, :name], :unique => true, :null => true
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Credential used to prove the identity of a user.
|
2
|
+
class Credential < ActiveRecord::Base
|
3
|
+
include Authpwn::CredentialModel
|
4
|
+
|
5
|
+
# Add your extensions to the Credential class here.
|
6
|
+
end
|
7
|
+
|
8
|
+
# Load built-in credential types, such as Email and Password.
|
9
|
+
require 'authpwn_rails/credentials.rb'
|
10
|
+
|
11
|
+
# namespace for all Credential subclasses
|
12
|
+
module Credentials
|
13
|
+
|
14
|
+
# Add your custom Credential types here.
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
jane_email:
|
2
|
+
type: Credentials::Email
|
3
|
+
user: jane
|
4
|
+
name: jane@gmail.com
|
5
|
+
key: "1"
|
6
|
+
|
7
|
+
john_email:
|
8
|
+
type: Credentials::Email
|
9
|
+
user: john
|
10
|
+
name: john@gmail.com
|
11
|
+
key: "0"
|
12
|
+
|
13
|
+
jane_password:
|
14
|
+
type: Credentials::Password
|
15
|
+
user: jane
|
16
|
+
key: <%= Credentials::Password.hash_password('pa55w0rd', '5678').inspect %>
|
17
|
+
|
18
|
+
john_password:
|
19
|
+
type: Credentials::Password
|
20
|
+
user: john
|
21
|
+
key: <%= Credentials::Password.hash_password('password', '1234').inspect %>
|
22
|
+
|
23
|
+
# Test account vic.tor@costan.us
|
24
|
+
jane_facebook:
|
25
|
+
user: jane
|
26
|
+
type: Credentials::Facebook
|
27
|
+
name: 1011950666
|
28
|
+
key: AAAEj8jKX2a8BAA4kNheRhOs6SlECVcZCE9o5pPKMytOjjoiNAoZBGZAwuL4KrrxXWesfJRhzDZCJiqrcQG3UdjRRNtyMJQMZD
|
29
|
+
|
30
|
+
john_facebook:
|
31
|
+
user: john
|
32
|
+
type: Credentials::Facebook
|
33
|
+
name: 702659
|
34
|
+
key: 702659|ffffffffffffffffffffffff-702659|ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
|
@@ -4,8 +4,8 @@
|
|
4
4
|
|
5
5
|
<% if current_user %>
|
6
6
|
<p>
|
7
|
-
You should inform the user that they are logged in as
|
8
|
-
<%= current_user.
|
7
|
+
You should inform the user that they are logged in as
|
8
|
+
<%= current_user.exuid %> and suggest them to
|
9
9
|
<%= link_to 'Log out', session_path, :method => :destroy %> and log in as a
|
10
10
|
different user.
|
11
11
|
</p>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<p>
|
2
2
|
This view gets displayed when the user is logged in. Right now,
|
3
|
-
<%= current_user.
|
3
|
+
user <%= current_user.exuid %> is logged in. You should allow the user to
|
4
4
|
<%= link_to 'Log out', session_path, :method => :destroy %>.
|
5
5
|
</p>
|
@@ -11,19 +11,19 @@
|
|
11
11
|
</p>
|
12
12
|
<% end %>
|
13
13
|
|
14
|
-
<%=
|
14
|
+
<%= form_tag :url => session_path do %>
|
15
15
|
<div class="field">
|
16
|
-
<%=
|
17
|
-
<%=
|
16
|
+
<%= label_tag :email, 'Email Address' %><br />
|
17
|
+
<%= email_field_tag :email, @email %>
|
18
18
|
</div>
|
19
19
|
|
20
20
|
<div class="field">
|
21
|
-
<%=
|
22
|
-
<%=
|
21
|
+
<%= label_tag :password %><br />
|
22
|
+
<%= password_field_tag :password %>
|
23
23
|
</div>
|
24
24
|
|
25
25
|
<div class="actions">
|
26
|
-
<%=
|
26
|
+
<%= submit_tag 'Log in' %>
|
27
27
|
|
28
28
|
<% if @redirect_url %>
|
29
29
|
<%= hidden_field_tag :redirect_url, @redirect_url %>
|
@@ -17,8 +17,8 @@ class SessionControllerTest < ActionController::TestCase
|
|
17
17
|
set_session_current_user @user
|
18
18
|
get :show, :format => 'json'
|
19
19
|
|
20
|
-
assert_equal @user.
|
21
|
-
ActiveSupport::JSON.decode(response.body)['user']['
|
20
|
+
assert_equal @user.exuid,
|
21
|
+
ActiveSupport::JSON.decode(response.body)['user']['exuid']
|
22
22
|
end
|
23
23
|
|
24
24
|
test "application welcome page" do
|
@@ -1,11 +1,8 @@
|
|
1
1
|
jane:
|
2
|
-
|
3
|
-
email_hash: <%= Digest::SHA2.hexdigest('jane@gmail.com') %>
|
4
|
-
password_salt: 5678
|
5
|
-
password_hash: <%= User.hash_password('pa55w0rd', '5678').inspect %>
|
2
|
+
exuid: 12345
|
6
3
|
|
7
4
|
john:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
exuid: 56789
|
6
|
+
|
7
|
+
bill:
|
8
|
+
exuid: 98765
|
@@ -8,13 +8,13 @@ class ActionController::Base
|
|
8
8
|
# trick. Model instances must implement id, and the model class must implement
|
9
9
|
# find_by_id.
|
10
10
|
def self.authenticates_using_session(options = {})
|
11
|
-
include
|
11
|
+
include Authpwn::ControllerInstanceMethods
|
12
12
|
before_filter :authenticate_using_session, options
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
# :nodoc: namespace
|
17
|
-
module
|
17
|
+
module Authpwn
|
18
18
|
|
19
19
|
# Included in controllers that call authenticates_using_session.
|
20
20
|
module ControllerInstanceMethods
|
@@ -23,15 +23,15 @@ module ControllerInstanceMethods
|
|
23
23
|
def current_user=(user)
|
24
24
|
@current_user = user
|
25
25
|
if user
|
26
|
-
session[:
|
26
|
+
session[:user_exuid] = user.to_param
|
27
27
|
else
|
28
|
-
session.delete :
|
28
|
+
session.delete :user_exuid
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def authenticate_using_session
|
33
33
|
return true if current_user
|
34
|
-
user_param = session[:
|
34
|
+
user_param = session[:user_exuid]
|
35
35
|
user = user_param && User.find_by_param(user_param)
|
36
36
|
self.current_user = user if user
|
37
37
|
end
|
@@ -63,6 +63,6 @@ module ControllerInstanceMethods
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
66
|
-
end # module
|
66
|
+
end # module Authpwn::ControllerInstanceMethods
|
67
67
|
|
68
|
-
end # namespace
|
68
|
+
end # namespace Authpwn
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
|
3
3
|
# :nodoc: namespace
|
4
|
-
module
|
4
|
+
module Authpwn
|
5
5
|
|
6
6
|
# Included by the controller that handles user authentication.
|
7
7
|
#
|
@@ -14,11 +14,11 @@ module SessionController
|
|
14
14
|
authenticates_using_session
|
15
15
|
end
|
16
16
|
|
17
|
-
# Included in controllers that include
|
17
|
+
# Included in controllers that include Authpwn::SessionController.
|
18
18
|
module InstanceMethods
|
19
19
|
# GET /session/new
|
20
20
|
def new
|
21
|
-
@
|
21
|
+
@email = params[:email]
|
22
22
|
@redirect_url = flash[:auth_redirect_url]
|
23
23
|
redirect_to session_url if current_user
|
24
24
|
end
|
@@ -52,25 +52,27 @@ module SessionController
|
|
52
52
|
|
53
53
|
# POST /session
|
54
54
|
def create
|
55
|
-
@user = User.new params[:user]
|
56
55
|
@redirect_url = params[:redirect_url] || session_url
|
57
|
-
|
58
|
-
|
56
|
+
@email = params[:email]
|
57
|
+
self.current_user = Credentials::Password.authenticate_email(@email,
|
58
|
+
params[:password])
|
59
59
|
|
60
60
|
respond_to do |format|
|
61
61
|
if current_user
|
62
62
|
format.html { redirect_to @redirect_url }
|
63
63
|
format.json do
|
64
|
-
user_data =
|
65
|
-
|
64
|
+
user_data = current_user.as_json
|
65
|
+
if current_user.class.include_root_in_json
|
66
|
+
user_data = user_data['user']
|
67
|
+
end
|
66
68
|
render :json => { :user => user_data,
|
67
69
|
:csrf => form_authenticity_token }
|
68
70
|
end
|
69
71
|
else
|
70
72
|
notice = 'Invalid e-mail or password'
|
71
73
|
format.html do
|
72
|
-
redirect_to new_session_url, :flash => {
|
73
|
-
|
74
|
+
redirect_to new_session_url, :flash => { :notice => notice,
|
75
|
+
:auth_redirect_url => @redirect_url }
|
74
76
|
end
|
75
77
|
format.json { render :json => { :error => notice} }
|
76
78
|
end
|
@@ -95,8 +97,8 @@ module SessionController
|
|
95
97
|
def welcome
|
96
98
|
end
|
97
99
|
private :welcome
|
98
|
-
end # module
|
100
|
+
end # module Authpwn::SessionController::InstanceMethods
|
99
101
|
|
100
|
-
end # module
|
102
|
+
end # module Authpwn::SessionController
|
101
103
|
|
102
|
-
end # namespace
|
104
|
+
end # namespace Authpwn
|