omniauth-identity 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +77 -0
- data/Guardfile +10 -0
- data/README.markdown +90 -0
- data/Rakefile +9 -0
- data/lib/omniauth/identity/model.rb +115 -0
- data/lib/omniauth/identity/models/active_record.rb +24 -0
- data/lib/omniauth/identity/secure_password.rb +78 -0
- data/lib/omniauth/identity.rb +17 -0
- data/lib/omniauth/strategies/identity.rb +86 -0
- data/lib/omniauth-identity/version.rb +5 -0
- data/lib/omniauth-identity.rb +2 -0
- data/omniauth-identity.gemspec +27 -0
- data/spec/omniauth/identity/model_spec.rb +117 -0
- data/spec/omniauth/identity/models/active_record_spec.rb +16 -0
- data/spec/omniauth/identity/secure_password_spec.rb +27 -0
- data/spec/omniauth/strategies/identity_spec.rb +130 -0
- data/spec/spec_helper.rb +14 -0
- metadata +158 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
omniauth-identity (1.0.0.beta1)
|
5
|
+
omniauth (= 1.0.0.beta1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.0.10)
|
11
|
+
activesupport (= 3.0.10)
|
12
|
+
builder (~> 2.1.2)
|
13
|
+
i18n (~> 0.5.0)
|
14
|
+
activerecord (3.0.10)
|
15
|
+
activemodel (= 3.0.10)
|
16
|
+
activesupport (= 3.0.10)
|
17
|
+
arel (~> 2.0.10)
|
18
|
+
tzinfo (~> 0.3.23)
|
19
|
+
activesupport (3.0.10)
|
20
|
+
arel (2.0.10)
|
21
|
+
bcrypt-ruby (2.1.4)
|
22
|
+
builder (2.1.2)
|
23
|
+
diff-lcs (1.1.3)
|
24
|
+
growl (1.0.3)
|
25
|
+
guard (0.8.4)
|
26
|
+
thor (~> 0.14.6)
|
27
|
+
guard-bundler (0.1.3)
|
28
|
+
bundler (>= 1.0.0)
|
29
|
+
guard (>= 0.2.2)
|
30
|
+
guard-rspec (0.4.5)
|
31
|
+
guard (>= 0.4.0)
|
32
|
+
hashie (1.2.0)
|
33
|
+
i18n (0.5.0)
|
34
|
+
maruku (0.6.0)
|
35
|
+
syntax (>= 1.0.0)
|
36
|
+
multi_json (1.0.3)
|
37
|
+
omniauth (1.0.0.beta1)
|
38
|
+
hashie (~> 1.2)
|
39
|
+
rack
|
40
|
+
rack (1.3.5)
|
41
|
+
rack-test (0.6.1)
|
42
|
+
rack (>= 1.0)
|
43
|
+
rake (0.9.2)
|
44
|
+
rb-fsevent (0.4.3.1)
|
45
|
+
rspec (2.6.0)
|
46
|
+
rspec-core (~> 2.6.0)
|
47
|
+
rspec-expectations (~> 2.6.0)
|
48
|
+
rspec-mocks (~> 2.6.0)
|
49
|
+
rspec-core (2.6.4)
|
50
|
+
rspec-expectations (2.6.0)
|
51
|
+
diff-lcs (~> 1.1.2)
|
52
|
+
rspec-mocks (2.6.0)
|
53
|
+
simplecov (0.5.3)
|
54
|
+
multi_json (~> 1.0.3)
|
55
|
+
simplecov-html (~> 0.5.3)
|
56
|
+
simplecov-html (0.5.3)
|
57
|
+
syntax (1.0.0)
|
58
|
+
thor (0.14.6)
|
59
|
+
tzinfo (0.3.30)
|
60
|
+
|
61
|
+
PLATFORMS
|
62
|
+
ruby
|
63
|
+
|
64
|
+
DEPENDENCIES
|
65
|
+
activerecord (~> 3.0)
|
66
|
+
bcrypt-ruby (~> 2.1.4)
|
67
|
+
growl
|
68
|
+
guard
|
69
|
+
guard-bundler
|
70
|
+
guard-rspec
|
71
|
+
maruku (~> 0.6)
|
72
|
+
omniauth-identity!
|
73
|
+
rack-test (~> 0.5)
|
74
|
+
rake (~> 0.8)
|
75
|
+
rb-fsevent
|
76
|
+
rspec (~> 2.5)
|
77
|
+
simplecov (~> 0.4)
|
data/Guardfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# OmniAuth Identity
|
2
|
+
|
3
|
+
The OmniAuth Identity gem provides a way for applications to utilize a
|
4
|
+
traditional login/password based authentication system without the need
|
5
|
+
to give up the simple authentication flow provided by OmniAuth. Identity
|
6
|
+
is designed on purpose to be as featureless as possible: it provides the
|
7
|
+
basic construct for user management and then gets out of the way.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
You use `oa-identity` just like you would any other OmniAuth provider: as a
|
12
|
+
Rack middleware. The basic setup for a email/password authentication would
|
13
|
+
look something like this:
|
14
|
+
|
15
|
+
use OmniAuth::Builder do
|
16
|
+
provider :identity, :fields => [:email]
|
17
|
+
end
|
18
|
+
|
19
|
+
Next, you need to create a model (called `Identity by default`) that will be
|
20
|
+
able to persist the information provided by the user. Luckily for you, there
|
21
|
+
are pre-built models for popular ORMs that make this dead simple. You just
|
22
|
+
need to subclass the relevant class:
|
23
|
+
|
24
|
+
class Identity < OmniAuth::Identity::Models::ActiveRecord
|
25
|
+
# Add whatever you like!
|
26
|
+
end
|
27
|
+
|
28
|
+
Adapters are provided for `ActiveRecord` and `MongoMapper` and are
|
29
|
+
autoloaded on request (but not loaded by default so no dependencies are
|
30
|
+
injected).
|
31
|
+
|
32
|
+
Once you've got an Identity persistence model and the strategy up and
|
33
|
+
running, you can point users to `/auth/identity` and it will request
|
34
|
+
that they log in or give them the opportunity to sign up for an account.
|
35
|
+
Once they have authenticated with their identity, OmniAuth will call
|
36
|
+
through to `/auth/identity/callback` with the same kinds of information
|
37
|
+
it would had the user authenticated through an external provider.
|
38
|
+
Simple!
|
39
|
+
|
40
|
+
## Custom Auth Model
|
41
|
+
|
42
|
+
To use a class other than the default, specify the <tt>:model</tt> option to a
|
43
|
+
different class.
|
44
|
+
|
45
|
+
use OmniAuth::Builder do
|
46
|
+
provider :identity, :fields => [:email], :model => MyCustomClass
|
47
|
+
end
|
48
|
+
|
49
|
+
## Customizing Registration Failure
|
50
|
+
|
51
|
+
To use your own custom registration form, create a form that POSTs to
|
52
|
+
'/auth/identity/register' with 'password', 'password_confirmation', and your
|
53
|
+
other fields.
|
54
|
+
|
55
|
+
<%= form_tag '/auth/identity/register' do |f| %>
|
56
|
+
<h1>Create an Account</h1>
|
57
|
+
<%= text_field_tag :email %>
|
58
|
+
<%= password_field_tag, :password %>
|
59
|
+
<%= password_field_tag, :password_confirmation %>
|
60
|
+
<%= submit_tag %>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
Beware not to nest your form parameters within a namespace. This strategy
|
64
|
+
looks for the form parameters at the top level of the post params. If you are
|
65
|
+
using [simple\_form](https://github.com/plataformatec/simple_form), then you
|
66
|
+
can avoid the params nesting by specifying <tt>:input_html</tt>.
|
67
|
+
|
68
|
+
<%= simple_form_for @identity, :url => '/auth/identity/register' do |f| %>
|
69
|
+
<h1>Create an Account</h1>
|
70
|
+
<%# specify :input_html to avoid params nesting %>
|
71
|
+
<%= f.input :email, :input_html => {:name => 'email'} %>
|
72
|
+
<%= f.input :password, :as => 'password', :input_html => {:name => 'password'} %>
|
73
|
+
<%= f.input :password_confirmation, :label => "Confirm Password", :as => 'password', :input_html => {:name => 'password_confirmation'} %>
|
74
|
+
<button type='submit'>Sign Up</button>
|
75
|
+
<% end %>
|
76
|
+
|
77
|
+
Next you'll need to let OmniAuth know what action to call when a registration
|
78
|
+
fails. In your OmniAuth configuration, specify any valid rack endpoint in the
|
79
|
+
<tt>:on_failed_registration</tt> option.
|
80
|
+
|
81
|
+
use OmniAuth::Builder do
|
82
|
+
provider :identity,
|
83
|
+
:fields => [:email],
|
84
|
+
:on_failed_registration => UsersController.action(:new)
|
85
|
+
end
|
86
|
+
|
87
|
+
For more information on rack endpoints, check out [this
|
88
|
+
introduction](http://library.edgecase.com/Rails/2011/01/04/rails-routing-and-rack-endpoints.html)
|
89
|
+
and
|
90
|
+
[ActionController::Metal](http://rubydoc.info/docs/rails/ActionController/Metal)
|
data/Rakefile
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Identity
|
3
|
+
# This module provides an includable interface for implementing the
|
4
|
+
# necessary API for OmniAuth Identity to properly locate identities
|
5
|
+
# and provide all necessary information. All methods marked as
|
6
|
+
# abstract must be implemented in the including class for things to
|
7
|
+
# work properly.
|
8
|
+
module Model
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# Locate an identity given its unique login key.
|
15
|
+
#
|
16
|
+
# @abstract
|
17
|
+
# @param [String] key The unique login key.
|
18
|
+
# @return [Model] An instance of the identity model class.
|
19
|
+
def locate(key)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Authenticate a user with the given key and password.
|
24
|
+
#
|
25
|
+
# @param [String] key The unique login key provided for a given identity.
|
26
|
+
# @param [String] password The presumed password for the identity.
|
27
|
+
# @return [Model] An instance of the identity model class.
|
28
|
+
def authenticate(key, password)
|
29
|
+
instance = locate(key)
|
30
|
+
return false unless instance
|
31
|
+
instance.authenticate(password)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Used to set or retrieve the method that will be used to get
|
35
|
+
# and set the user-supplied authentication key.
|
36
|
+
# @return [String] The method name.
|
37
|
+
def auth_key(method = false)
|
38
|
+
@auth_key = method.to_s unless method == false
|
39
|
+
@auth_key = nil if @auth_key == ''
|
40
|
+
|
41
|
+
@auth_key || 'email'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns self if the provided password is correct, false
|
46
|
+
# otherwise.
|
47
|
+
#
|
48
|
+
# @abstract
|
49
|
+
# @param [String] password The password to check.
|
50
|
+
# @return [self or false] Self if authenticated, false if not.
|
51
|
+
def authenticate(password)
|
52
|
+
raise NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
SCHEMA_ATTRIBUTES = %w(name email nickname first_name last_name location description image phone)
|
56
|
+
# A hash of as much of the standard OmniAuth schema as is stored
|
57
|
+
# in this particular model. By default, this will call instance
|
58
|
+
# methods for each of the attributes it needs in turn, ignoring
|
59
|
+
# any for which `#respond_to?` is `false`.
|
60
|
+
#
|
61
|
+
# If `first_name`, `nickname`, and/or `last_name` is provided but
|
62
|
+
# `name` is not, it will be automatically calculated.
|
63
|
+
#
|
64
|
+
# @return [Hash] A string-keyed hash of user information.
|
65
|
+
def info
|
66
|
+
info = SCHEMA_ATTRIBUTES.inject({}) do |hash,attribute|
|
67
|
+
hash[attribute] = send(attribute) if respond_to?(attribute)
|
68
|
+
hash
|
69
|
+
end
|
70
|
+
info
|
71
|
+
end
|
72
|
+
|
73
|
+
# An identifying string that must be globally unique to the
|
74
|
+
# application. Defaults to stringifying the `id` method.
|
75
|
+
#
|
76
|
+
# @return [String] An identifier string unique to this identity.
|
77
|
+
def uid
|
78
|
+
if respond_to?('id')
|
79
|
+
return nil if self.id.nil?
|
80
|
+
self.id.to_s
|
81
|
+
else
|
82
|
+
raise NotImplementedError
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Used to retrieve the user-supplied authentication key (e.g. a
|
87
|
+
# username or email). Determined using the class method of the same name,
|
88
|
+
# defaults to `:email`.
|
89
|
+
#
|
90
|
+
# @return [String] An identifying string that will be entered by
|
91
|
+
# users upon sign in.
|
92
|
+
def auth_key
|
93
|
+
if respond_to?(self.class.auth_key)
|
94
|
+
send(self.class.auth_key)
|
95
|
+
else
|
96
|
+
raise NotImplementedError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Used to set the user-supplied authentication key (e.g. a
|
101
|
+
# username or email. Determined using the `.auth_key` class
|
102
|
+
# method.
|
103
|
+
#
|
104
|
+
# @param [String] value The value to which the auth key should be
|
105
|
+
# set.
|
106
|
+
def auth_key=(value)
|
107
|
+
if respond_to?(self.class.auth_key + '=')
|
108
|
+
send(self.class.auth_key + '=', value)
|
109
|
+
else
|
110
|
+
raise NotImplementedError
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module OmniAuth
|
4
|
+
module Identity
|
5
|
+
module Models
|
6
|
+
class ActiveRecord < ::ActiveRecord::Base
|
7
|
+
include OmniAuth::Identity::Model
|
8
|
+
include OmniAuth::Identity::SecurePassword
|
9
|
+
|
10
|
+
self.abstract_class = true
|
11
|
+
has_secure_password
|
12
|
+
|
13
|
+
def self.auth_key=(key)
|
14
|
+
super
|
15
|
+
validates_uniqueness_of key, :case_sensitive => false
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.locate(key)
|
19
|
+
where(auth_key => key).first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'bcrypt'
|
2
|
+
|
3
|
+
module OmniAuth
|
4
|
+
module Identity
|
5
|
+
# This is taken directly from Rails 3.1 code and is used if
|
6
|
+
# the version of ActiveModel that's being used does not
|
7
|
+
# include SecurePassword. The only difference is that instead of
|
8
|
+
# using ActiveSupport::Concern, it checks to see if there is already
|
9
|
+
# a has_secure_password method.
|
10
|
+
module SecurePassword
|
11
|
+
def self.included(base)
|
12
|
+
unless base.respond_to?(:has_secure_password)
|
13
|
+
base.extend ClassMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Adds methods to set and authenticate against a BCrypt password.
|
19
|
+
# This mechanism requires you to have a password_digest attribute.
|
20
|
+
#
|
21
|
+
# Validations for presence of password, confirmation of password (using
|
22
|
+
# a "password_confirmation" attribute) are automatically added.
|
23
|
+
# You can add more validations by hand if need be.
|
24
|
+
#
|
25
|
+
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
|
26
|
+
#
|
27
|
+
# # Schema: User(name:string, password_digest:string)
|
28
|
+
# class User < ActiveRecord::Base
|
29
|
+
# has_secure_password
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch")
|
33
|
+
# user.save # => false, password required
|
34
|
+
# user.password = "mUc3m00RsqyRe"
|
35
|
+
# user.save # => false, confirmation doesn't match
|
36
|
+
# user.password_confirmation = "mUc3m00RsqyRe"
|
37
|
+
# user.save # => true
|
38
|
+
# user.authenticate("notright") # => false
|
39
|
+
# user.authenticate("mUc3m00RsqyRe") # => user
|
40
|
+
# User.find_by_name("david").try(:authenticate, "notright") # => nil
|
41
|
+
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
|
42
|
+
def has_secure_password
|
43
|
+
attr_reader :password
|
44
|
+
|
45
|
+
validates_confirmation_of :password
|
46
|
+
validates_presence_of :password_digest
|
47
|
+
|
48
|
+
include InstanceMethodsOnActivation
|
49
|
+
|
50
|
+
if respond_to?(:attributes_protected_by_default)
|
51
|
+
def self.attributes_protected_by_default
|
52
|
+
super + ['password_digest']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module InstanceMethodsOnActivation
|
59
|
+
# Returns self if the password is correct, otherwise false.
|
60
|
+
def authenticate(unencrypted_password)
|
61
|
+
if BCrypt::Password.new(password_digest) == unencrypted_password
|
62
|
+
self
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Encrypts the password into the password_digest attribute.
|
69
|
+
def password=(unencrypted_password)
|
70
|
+
@password = unencrypted_password
|
71
|
+
unless unencrypted_password.blank?
|
72
|
+
self.password_digest = BCrypt::Password.create(unencrypted_password)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'omniauth'
|
2
|
+
|
3
|
+
module OmniAuth
|
4
|
+
module Strategies
|
5
|
+
autoload :Identity, 'omniauth/strategies/identity'
|
6
|
+
end
|
7
|
+
|
8
|
+
module Identity
|
9
|
+
autoload :Model, 'omniauth/identity/model'
|
10
|
+
autoload :SecurePassword, 'omniauth/identity/secure_password'
|
11
|
+
module Models
|
12
|
+
autoload :ActiveRecord, 'omniauth/identity/models/active_record'
|
13
|
+
# autoload :MongoMapper, 'omniauth/identity/models/mongo_mapper'
|
14
|
+
# autoload :Mongoid, 'omniauth/identity/models/mongoid'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Strategies
|
3
|
+
# The identity strategy allows you to provide simple internal
|
4
|
+
# user authentication using the same process flow that you
|
5
|
+
# use for external OmniAuth providers.
|
6
|
+
class Identity
|
7
|
+
include OmniAuth::Strategy
|
8
|
+
|
9
|
+
option :fields, [:name, :email]
|
10
|
+
option :on_failed_registration, nil
|
11
|
+
|
12
|
+
def request_phase
|
13
|
+
OmniAuth::Form.build(
|
14
|
+
:title => (options[:title] || "Identity Verification"),
|
15
|
+
:url => callback_path
|
16
|
+
) do |f|
|
17
|
+
f.text_field 'Login', 'auth_key'
|
18
|
+
f.password_field 'Password', 'password'
|
19
|
+
f.html "<p align='center'><a href='#{registration_path}'>Create an Identity</a></p>"
|
20
|
+
end.to_response
|
21
|
+
end
|
22
|
+
|
23
|
+
def callback_phase
|
24
|
+
return fail!(:invalid_credentials) unless identity
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def other_phase
|
29
|
+
if on_registration_path?
|
30
|
+
if request.get?
|
31
|
+
registration_form
|
32
|
+
elsif request.post?
|
33
|
+
registration_phase
|
34
|
+
end
|
35
|
+
else
|
36
|
+
call_app!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def registration_form
|
41
|
+
OmniAuth::Form.build(:title => 'Register Identity') do |f|
|
42
|
+
options[:fields].each do |field|
|
43
|
+
f.text_field field.to_s.capitalize, field.to_s
|
44
|
+
end
|
45
|
+
f.password_field 'Password', 'password'
|
46
|
+
f.password_field 'Confirm Password', 'password_confirmation'
|
47
|
+
end.to_response
|
48
|
+
end
|
49
|
+
|
50
|
+
def registration_phase
|
51
|
+
attributes = (options[:fields] + [:password, :password_confirmation]).inject({}){|h,k| h[k] = request[k.to_s]; h}
|
52
|
+
@identity = model.create(attributes)
|
53
|
+
if @identity.persisted?
|
54
|
+
env['PATH_INFO'] = callback_path
|
55
|
+
callback_phase
|
56
|
+
else
|
57
|
+
if options[:on_failed_registration]
|
58
|
+
self.env['omniauth.identity'] = @identity
|
59
|
+
options[:on_failed_registration].call(self.env)
|
60
|
+
else
|
61
|
+
registration_form
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
uid{ identity.uid }
|
67
|
+
info{ identity.info }
|
68
|
+
|
69
|
+
def registration_path
|
70
|
+
options[:registration_path] || "#{path_prefix}/#{name}/register"
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_registration_path?
|
74
|
+
on_path?(registration_path)
|
75
|
+
end
|
76
|
+
|
77
|
+
def identity
|
78
|
+
@identity ||= model.authenticate(request['auth_key'], request['password'])
|
79
|
+
end
|
80
|
+
|
81
|
+
def model
|
82
|
+
options[:model] || ::Identity
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.dirname(__FILE__) + '/lib/omniauth-identity/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.add_runtime_dependency 'omniauth', '1.0.0.beta1'
|
6
|
+
|
7
|
+
gem.add_development_dependency 'maruku', '~> 0.6'
|
8
|
+
gem.add_development_dependency 'simplecov', '~> 0.4'
|
9
|
+
gem.add_development_dependency 'rack-test', '~> 0.5'
|
10
|
+
gem.add_development_dependency 'rake', '~> 0.8'
|
11
|
+
gem.add_development_dependency 'rspec', '~> 2.5'
|
12
|
+
gem.add_development_dependency 'bcrypt-ruby', '~> 2.1.4'
|
13
|
+
gem.add_development_dependency 'activerecord', '~> 3.0'
|
14
|
+
|
15
|
+
gem.name = 'omniauth-identity'
|
16
|
+
gem.version = OmniAuth::Identity::VERSION
|
17
|
+
gem.description = %q{Internal authentication handlers for OmniAuth.}
|
18
|
+
gem.summary = gem.description
|
19
|
+
gem.email = ['michael@intridea.com']
|
20
|
+
gem.homepage = 'http://github.com/intridea/omniauth-identity'
|
21
|
+
gem.authors = ['Michael Bleigh']
|
22
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
23
|
+
gem.files = `git ls-files`.split("\n")
|
24
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
gem.require_paths = ['lib']
|
26
|
+
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
|
27
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class ExampleModel
|
4
|
+
include OmniAuth::Identity::Model
|
5
|
+
end
|
6
|
+
|
7
|
+
describe OmniAuth::Identity::Model do
|
8
|
+
context 'Class Methods' do
|
9
|
+
subject{ ExampleModel }
|
10
|
+
|
11
|
+
describe '.locate' do
|
12
|
+
it('should be abstract'){ lambda{ subject.locate('abc') }.should raise_error(NotImplementedError) }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.authenticate' do
|
16
|
+
it 'should call locate and then authenticate' do
|
17
|
+
mocked_instance = mock('ExampleModel', :authenticate => 'abbadoo')
|
18
|
+
subject.should_receive(:locate).with('example').and_return(mocked_instance)
|
19
|
+
subject.authenticate('example','pass').should == 'abbadoo'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should recover gracefully if locate is nil' do
|
23
|
+
subject.stub!(:locate).and_return(nil)
|
24
|
+
subject.authenticate('blah','foo').should be_false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'Instance Methods' do
|
30
|
+
subject{ ExampleModel.new }
|
31
|
+
|
32
|
+
describe '#authenticate' do
|
33
|
+
it('should be abstract'){ lambda{ subject.authenticate('abc') }.should raise_error(NotImplementedError) }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#uid' do
|
37
|
+
it 'should default to #id' do
|
38
|
+
subject.should_receive(:respond_to?).with('id').and_return(true)
|
39
|
+
subject.stub!(:id).and_return 'wakka-do'
|
40
|
+
subject.uid.should == 'wakka-do'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should stringify it' do
|
44
|
+
subject.stub!(:id).and_return 123
|
45
|
+
subject.uid.should == '123'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should raise NotImplementedError if #id is not defined' do
|
49
|
+
subject.should_receive(:respond_to?).with('id').and_return(false)
|
50
|
+
lambda{ subject.uid }.should raise_error(NotImplementedError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#auth_key' do
|
55
|
+
it 'should default to #email' do
|
56
|
+
subject.should_receive(:respond_to?).with('email').and_return(true)
|
57
|
+
subject.stub!(:email).and_return('bob@bob.com')
|
58
|
+
subject.auth_key.should == 'bob@bob.com'
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should use the class .auth_key' do
|
62
|
+
subject.class.auth_key 'login'
|
63
|
+
subject.stub!(:login).and_return 'bob'
|
64
|
+
subject.auth_key.should == 'bob'
|
65
|
+
subject.class.auth_key nil
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should raise a NotImplementedError if the auth_key method is not defined' do
|
69
|
+
lambda{ subject.auth_key }.should raise_error(NotImplementedError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#auth_key=' do
|
74
|
+
it 'should default to setting email' do
|
75
|
+
subject.should_receive(:respond_to?).with('email=').and_return(true)
|
76
|
+
subject.should_receive(:email=).with 'abc'
|
77
|
+
|
78
|
+
subject.auth_key = 'abc'
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should use a custom .auth_key if one is provided' do
|
82
|
+
subject.class.auth_key 'login'
|
83
|
+
subject.should_receive(:respond_to?).with('login=').and_return(true)
|
84
|
+
subject.should_receive('login=').with('abc')
|
85
|
+
|
86
|
+
subject.auth_key = 'abc'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should raise a NotImplementedError if the autH_key method is not defined' do
|
90
|
+
lambda{ subject.auth_key = 'broken' }.should raise_error(NotImplementedError)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#info' do
|
95
|
+
it 'should include attributes that are set' do
|
96
|
+
subject.stub!(:name).and_return('Bob Bobson')
|
97
|
+
subject.stub!(:nickname).and_return('bob')
|
98
|
+
|
99
|
+
subject.info.should == {
|
100
|
+
'name' => 'Bob Bobson',
|
101
|
+
'nickname' => 'bob'
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should automatically set name off of nickname' do
|
106
|
+
subject.stub!(:nickname).and_return('bob')
|
107
|
+
subject.info['name'] == 'bob'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should not overwrite a provided name' do
|
111
|
+
subject.stub!(:name).and_return('Awesome Dude')
|
112
|
+
subject.stub!(:first_name).and_return('Frank')
|
113
|
+
subject.info['name'].should == 'Awesome Dude'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe(OmniAuth::Identity::Models::ActiveRecord, :db => true) do
|
4
|
+
class TestIdentity < OmniAuth::Identity::Models::ActiveRecord
|
5
|
+
auth_key :ham_sandwich
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should locate using the auth key using a where query' do
|
9
|
+
TestIdentity.should_receive(:where).with('ham_sandwich' => 'open faced').and_return(['wakka'])
|
10
|
+
TestIdentity.locate('open faced').should == 'wakka'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should not use STI rules for its table name' do
|
14
|
+
TestIdentity.table_name.should == 'test_identities'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class HasTheMethod
|
4
|
+
def self.has_secure_password; end
|
5
|
+
end
|
6
|
+
|
7
|
+
class DoesNotHaveTheMethod
|
8
|
+
end
|
9
|
+
|
10
|
+
describe OmniAuth::Identity::SecurePassword do
|
11
|
+
it 'should extend with the class methods if it does not have the method' do
|
12
|
+
DoesNotHaveTheMethod.should_receive(:extend).with(OmniAuth::Identity::SecurePassword::ClassMethods)
|
13
|
+
DoesNotHaveTheMethod.send(:include, OmniAuth::Identity::SecurePassword)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should not extend if the method is already defined' do
|
17
|
+
HasTheMethod.should_not_receive(:extend)
|
18
|
+
HasTheMethod.send(:include, OmniAuth::Identity::SecurePassword)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should respond to has_secure_password afterwards' do
|
22
|
+
[HasTheMethod,DoesNotHaveTheMethod].each do |klass|
|
23
|
+
klass.send(:include, OmniAuth::Identity::SecurePassword)
|
24
|
+
klass.should be_respond_to(:has_secure_password)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MockIdentity; end
|
4
|
+
|
5
|
+
describe OmniAuth::Strategies::Identity do
|
6
|
+
attr_accessor :app
|
7
|
+
|
8
|
+
let(:auth_hash){ last_response.headers['env']['omniauth.auth'] }
|
9
|
+
let(:identity_hash){ last_response.headers['env']['omniauth.identity'] }
|
10
|
+
|
11
|
+
# customize rack app for testing, if block is given, reverts to default
|
12
|
+
# rack app after testing is done
|
13
|
+
def set_app!(identity_options = {})
|
14
|
+
identity_options = {:model => MockIdentity}.merge(identity_options)
|
15
|
+
old_app = self.app
|
16
|
+
self.app = Rack::Builder.app do
|
17
|
+
use Rack::Session::Cookie
|
18
|
+
use OmniAuth::Strategies::Identity, identity_options
|
19
|
+
run lambda{|env| [404, {'env' => env}, ["HELLO!"]]}
|
20
|
+
end
|
21
|
+
if block_given?
|
22
|
+
yield
|
23
|
+
self.app = old_app
|
24
|
+
end
|
25
|
+
self.app
|
26
|
+
end
|
27
|
+
|
28
|
+
before(:all) do
|
29
|
+
set_app!
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#request_phase' do
|
33
|
+
it 'should display a form' do
|
34
|
+
get '/auth/identity'
|
35
|
+
last_response.body.should be_include("<form")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#callback_phase' do
|
40
|
+
let(:user){ mock(:uid => 'user1', :info => {'name' => 'Rockefeller'})}
|
41
|
+
|
42
|
+
context 'with valid credentials' do
|
43
|
+
before do
|
44
|
+
MockIdentity.should_receive('authenticate').with('john','awesome').and_return(user)
|
45
|
+
post '/auth/identity/callback', :auth_key => 'john', :password => 'awesome'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should populate the auth hash' do
|
49
|
+
auth_hash.should be_kind_of(Hash)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should populate the uid' do
|
53
|
+
auth_hash['uid'].should == 'user1'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should populate the info hash' do
|
57
|
+
auth_hash['info'].should == {'name' => 'Rockefeller'}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with invalid credentials' do
|
62
|
+
before do
|
63
|
+
OmniAuth.config.on_failure = lambda{|env| [401, {}, [env['omniauth.error.type'].inspect]]}
|
64
|
+
MockIdentity.should_receive(:authenticate).with('wrong','login').and_return(false)
|
65
|
+
post '/auth/identity/callback', :auth_key => 'wrong', :password => 'login'
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should fail with :invalid_credentials' do
|
69
|
+
last_response.body.should == ':invalid_credentials'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#registration_form' do
|
75
|
+
it 'should trigger from /auth/identity/register by default' do
|
76
|
+
get '/auth/identity/register'
|
77
|
+
last_response.body.should be_include("Register Identity")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#registration_phase' do
|
82
|
+
context 'with successful creation' do
|
83
|
+
let(:properties){ {
|
84
|
+
:name => 'Awesome Dude',
|
85
|
+
:email => 'awesome@example.com',
|
86
|
+
:password => 'face',
|
87
|
+
:password_confirmation => 'face'
|
88
|
+
} }
|
89
|
+
|
90
|
+
before do
|
91
|
+
m = mock(:uid => 'abc', :name => 'Awesome Dude', :email => 'awesome@example.com', :info => {:name => 'DUUUUDE!'}, :persisted? => true)
|
92
|
+
MockIdentity.should_receive(:create).with(properties).and_return(m)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should set the auth hash' do
|
96
|
+
post '/auth/identity/register', properties
|
97
|
+
auth_hash['uid'].should == 'abc'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'with invalid identity' do
|
102
|
+
let(:properties) { {
|
103
|
+
:name => 'Awesome Dude',
|
104
|
+
:email => 'awesome@example.com',
|
105
|
+
:password => 'NOT',
|
106
|
+
:password_confirmation => 'MATCHING'
|
107
|
+
} }
|
108
|
+
|
109
|
+
before do
|
110
|
+
MockIdentity.should_receive(:create).with(properties).and_return(mock(:persisted? => false))
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'default' do
|
114
|
+
it 'should show registration form' do
|
115
|
+
post '/auth/identity/register', properties
|
116
|
+
last_response.body.should be_include("Register Identity")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'custom on_failed_registration endpoint' do
|
121
|
+
it 'should set the identity hash' do
|
122
|
+
set_app!(:on_failed_registration => lambda{|env| [404, {'env' => env}, ["HELLO!"]]}) do
|
123
|
+
post '/auth/identity/register', properties
|
124
|
+
identity_hash.should_not be_nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup :default, :development, :test
|
4
|
+
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
|
8
|
+
require 'rack/test'
|
9
|
+
require 'omniauth/identity'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.include Rack::Test::Methods
|
13
|
+
end
|
14
|
+
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omniauth-identity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.beta1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michael Bleigh
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-19 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: omniauth
|
16
|
+
requirement: &70239729775760 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0.beta1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70239729775760
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: maruku
|
27
|
+
requirement: &70239729775260 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.6'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70239729775260
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: simplecov
|
38
|
+
requirement: &70239729774800 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.4'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70239729774800
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack-test
|
49
|
+
requirement: &70239729774340 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.5'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70239729774340
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rake
|
60
|
+
requirement: &70239729790260 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0.8'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70239729790260
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: &70239729789800 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '2.5'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70239729789800
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: bcrypt-ruby
|
82
|
+
requirement: &70239729789340 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 2.1.4
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70239729789340
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: activerecord
|
93
|
+
requirement: &70239729788880 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ~>
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '3.0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70239729788880
|
102
|
+
description: Internal authentication handlers for OmniAuth.
|
103
|
+
email:
|
104
|
+
- michael@intridea.com
|
105
|
+
executables: []
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files: []
|
108
|
+
files:
|
109
|
+
- .gitignore
|
110
|
+
- .rspec
|
111
|
+
- Gemfile
|
112
|
+
- Gemfile.lock
|
113
|
+
- Guardfile
|
114
|
+
- README.markdown
|
115
|
+
- Rakefile
|
116
|
+
- lib/omniauth-identity.rb
|
117
|
+
- lib/omniauth-identity/version.rb
|
118
|
+
- lib/omniauth/identity.rb
|
119
|
+
- lib/omniauth/identity/model.rb
|
120
|
+
- lib/omniauth/identity/models/active_record.rb
|
121
|
+
- lib/omniauth/identity/secure_password.rb
|
122
|
+
- lib/omniauth/strategies/identity.rb
|
123
|
+
- omniauth-identity.gemspec
|
124
|
+
- spec/omniauth/identity/model_spec.rb
|
125
|
+
- spec/omniauth/identity/models/active_record_spec.rb
|
126
|
+
- spec/omniauth/identity/secure_password_spec.rb
|
127
|
+
- spec/omniauth/strategies/identity_spec.rb
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
homepage: http://github.com/intridea/omniauth-identity
|
130
|
+
licenses: []
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ! '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ! '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 1.3.6
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 1.8.10
|
150
|
+
signing_key:
|
151
|
+
specification_version: 3
|
152
|
+
summary: Internal authentication handlers for OmniAuth.
|
153
|
+
test_files:
|
154
|
+
- spec/omniauth/identity/model_spec.rb
|
155
|
+
- spec/omniauth/identity/models/active_record_spec.rb
|
156
|
+
- spec/omniauth/identity/secure_password_spec.rb
|
157
|
+
- spec/omniauth/strategies/identity_spec.rb
|
158
|
+
- spec/spec_helper.rb
|