authpwn_rails 0.9.6 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.travis.yml +6 -0
  2. data/Gemfile +3 -2
  3. data/Gemfile.lock +38 -36
  4. data/README.rdoc +6 -11
  5. data/VERSION +1 -1
  6. data/authpwn_rails.gemspec +30 -22
  7. data/lib/authpwn_rails.rb +2 -2
  8. data/lib/authpwn_rails/credential_model.rb +38 -0
  9. data/lib/authpwn_rails/credentials.rb +10 -0
  10. data/lib/authpwn_rails/credentials/email.rb +30 -0
  11. data/lib/authpwn_rails/credentials/facebook.rb +77 -0
  12. data/lib/authpwn_rails/credentials/password.rb +63 -0
  13. data/lib/authpwn_rails/engine.rb +5 -7
  14. data/lib/authpwn_rails/facebook_session.rb +5 -5
  15. data/lib/authpwn_rails/generators/{session_generator.rb → all_generator.rb} +28 -9
  16. data/lib/authpwn_rails/generators/templates/001_create_users.rb +3 -11
  17. data/lib/authpwn_rails/generators/templates/002_create_credentials.rb +19 -0
  18. data/lib/authpwn_rails/generators/templates/credential.rb +16 -0
  19. data/lib/authpwn_rails/generators/templates/credentials.yml +34 -0
  20. data/lib/authpwn_rails/generators/templates/session/forbidden.html.erb +2 -2
  21. data/lib/authpwn_rails/generators/templates/session/home.html.erb +1 -1
  22. data/lib/authpwn_rails/generators/templates/session/new.html.erb +6 -6
  23. data/lib/authpwn_rails/generators/templates/session_controller.rb +1 -1
  24. data/lib/authpwn_rails/generators/templates/session_controller_test.rb +2 -2
  25. data/lib/authpwn_rails/generators/templates/user.rb +2 -2
  26. data/lib/authpwn_rails/generators/templates/users.yml +5 -8
  27. data/lib/authpwn_rails/session.rb +7 -7
  28. data/lib/authpwn_rails/session_controller.rb +15 -13
  29. data/lib/authpwn_rails/test_extensions.rb +6 -6
  30. data/lib/authpwn_rails/user_model.rb +23 -92
  31. data/test/email_credential_test.rb +50 -0
  32. data/test/facebook_controller_test.rb +7 -2
  33. data/test/facebook_credential_test.rb +74 -0
  34. data/test/helpers/db_setup.rb +4 -4
  35. data/test/helpers/fbgraph.rb +6 -2
  36. data/test/password_credential_test.rb +67 -0
  37. data/test/session_controller_api_test.rb +12 -12
  38. data/test/test_helper.rb +1 -0
  39. data/test/user_test.rb +11 -100
  40. metadata +41 -25
  41. data/lib/authpwn_rails/facebook_token_model.rb +0 -66
  42. data/lib/authpwn_rails/generators/facebook_generator.rb +0 -18
  43. data/lib/authpwn_rails/generators/templates/002_create_facebook_tokens.rb +0 -15
  44. data/lib/authpwn_rails/generators/templates/facebook_token.rb +0 -6
  45. data/lib/authpwn_rails/generators/templates/facebook_tokens.yml +0 -10
  46. data/lib/authpwn_rails/generators/users_generator.rb +0 -16
  47. 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
@@ -2,13 +2,11 @@ require 'authpwn_rails'
2
2
  require 'rails'
3
3
 
4
4
  # :nodoc: namespace
5
- module AuthpwnRails
5
+ module Authpwn
6
6
 
7
7
  class Engine < Rails::Engine
8
8
  generators do
9
- require 'authpwn_rails/generators/facebook_generator.rb'
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 AuthpwnRails::TestExtensions
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 AuthpwnRails::Engine
23
+ end # class Authpwn::Engine
26
24
 
27
- end # namespace AuthpwnRails
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 AuthpwnRails::FacebookControllerInstanceMethods
11
+ include Authpwn::FacebookControllerInstanceMethods
12
12
  before_filter :authenticate_using_facebook_access_token, options
13
13
  end
14
- end # module AuthpwnRails::FacebookExtensions::ControllerClassMethods
14
+ end # module Authpwn::FacebookExtensions::ControllerClassMethods
15
15
 
16
16
  # :nodoc: namespace
17
- module AuthpwnRails
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 AuthpwnRails::FacebookControllerInstanceMethods
31
+ end # module Authpwn::FacebookControllerInstanceMethods
32
32
 
33
- end # namespace AuthpwnRails
33
+ end # namespace Authpwn
@@ -1,13 +1,36 @@
1
1
  # :nodoc: namespace
2
2
  module Authpwn
3
3
 
4
- # rails g authpwn:session
5
- class SessionGenerator < Rails::Generators::Base
4
+ # rails g authpwn:all
5
+ class AllGenerator < Rails::Generators::Base
6
6
  source_root File.expand_path("../templates", __FILE__)
7
7
 
8
- def create_session
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
- copy_file File.join('session_controller_test.rb'),
20
- File.join('test', 'functional', 'session_controller_test.rb')
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 self.up
2
+ def change
3
3
  create_table :users do |t|
4
- t.string :email, :limit => 128, :null => false
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, :email, :unique => true, :null => false
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.email %> and suggest them to
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.email %> is logged in. You should allow the user to
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
- <%= form_for @user, :url => session_path do |f| %>
14
+ <%= form_tag :url => session_path do %>
15
15
  <div class="field">
16
- <%= f.label :email, 'Email Address' %><br />
17
- <%= f.email_field :email %>
16
+ <%= label_tag :email, 'Email Address' %><br />
17
+ <%= email_field_tag :email, @email %>
18
18
  </div>
19
19
 
20
20
  <div class="field">
21
- <%= f.label :password %><br />
22
- <%= f.password_field :password %>
21
+ <%= label_tag :password %><br />
22
+ <%= password_field_tag :password %>
23
23
  </div>
24
24
 
25
25
  <div class="actions">
26
- <%= f.submit 'Log in' %>
26
+ <%= submit_tag 'Log in' %>
27
27
 
28
28
  <% if @redirect_url %>
29
29
  <%= hidden_field_tag :redirect_url, @redirect_url %>
@@ -1,6 +1,6 @@
1
1
  # Manages logging in and out of the application.
2
2
  class SessionController < ApplicationController
3
- include AuthpwnRails::SessionController
3
+ include Authpwn::SessionController
4
4
 
5
5
  # Sets up the 'session/welcome' view. No user is logged in.
6
6
  def welcome
@@ -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.email,
21
- ActiveSupport::JSON.decode(response.body)['user']['email']
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,6 +1,6 @@
1
1
  # An user account.
2
2
  class User < ActiveRecord::Base
3
- include AuthpwnRails::UserModel
3
+ include Authpwn::UserModel
4
4
 
5
- # Add your extensions to the User class here.
5
+ # Add your extensions to the User class here.
6
6
  end
@@ -1,11 +1,8 @@
1
1
  jane:
2
- email: jane@gmail.com
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
- email: john@gmail.com
9
- email_hash: <%= Digest::SHA2.hexdigest('john@gmail.com') %>
10
- password_salt: 1234
11
- password_hash: <%= User.hash_password('password', '1234').inspect %>
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 AuthpwnRails::ControllerInstanceMethods
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 AuthpwnRails
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[:current_user_pid] = user.to_param
26
+ session[:user_exuid] = user.to_param
27
27
  else
28
- session.delete :current_user_pid
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[:current_user_pid]
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 AuthpwnRails::ControllerInstanceMethods
66
+ end # module Authpwn::ControllerInstanceMethods
67
67
 
68
- end # namespace AuthpwnRails
68
+ end # namespace Authpwn
@@ -1,7 +1,7 @@
1
1
  require 'active_support'
2
2
 
3
3
  # :nodoc: namespace
4
- module AuthpwnRails
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 AuthpwnRails::SessionController.
17
+ # Included in controllers that include Authpwn::SessionController.
18
18
  module InstanceMethods
19
19
  # GET /session/new
20
20
  def new
21
- @user = User.new
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
- self.current_user =
58
- User.find_by_email_and_password @user.email, @user.password
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 = @user.as_json
65
- user_data = user_data['user'] if @user.class.include_root_in_json
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
- :notice => notice, :auth_redirect_url => @redirect_url }
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 AuthpwnRails::SessionController::InstanceMethods
100
+ end # module Authpwn::SessionController::InstanceMethods
99
101
 
100
- end # module AuthpwnRails::SessionController
102
+ end # module Authpwn::SessionController
101
103
 
102
- end # namespace AuthpwnRails
104
+ end # namespace Authpwn