omniauth-identity 1.0.0.beta1
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/.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
|