omniauth-identity 1.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module OmniAuth
2
2
  module Identity
3
- VERSION = '1.0.0'
3
+ VERSION = '3.0.1'
4
4
  end
5
5
  end
@@ -6,12 +6,12 @@ module OmniAuth
6
6
  end
7
7
 
8
8
  module Identity
9
- autoload :Model, 'omniauth/identity/model'
10
- autoload :SecurePassword, 'omniauth/identity/secure_password'
9
+ autoload :Model, 'omniauth/identity/model'
10
+ autoload :SecurePassword, 'omniauth/identity/secure_password'
11
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'
12
+ autoload :ActiveRecord, 'omniauth/identity/models/active_record'
13
+ autoload :Mongoid, 'omniauth/identity/models/mongoid'
14
+ autoload :CouchPotatoModule, 'omniauth/identity/models/couch_potato'
15
15
  end
16
16
  end
17
17
  end
@@ -23,14 +23,15 @@ module OmniAuth
23
23
  # Authenticate a user with the given key and password.
24
24
  #
25
25
  # @param [String] key The unique login key provided for a given identity.
26
- # @param [String] password The presumed password for the identity.
26
+ # @param [String] password The presumed password for the identity.
27
27
  # @return [Model] An instance of the identity model class.
28
- def authenticate(key, password)
29
- instance = locate(key)
28
+ def authenticate(conditions, password)
29
+ instance = locate(conditions)
30
30
  return false unless instance
31
+
31
32
  instance.authenticate(password)
32
33
  end
33
-
34
+
34
35
  # Used to set or retrieve the method that will be used to get
35
36
  # and set the user-supplied authentication key.
36
37
  # @return [String] The method name.
@@ -52,22 +53,20 @@ module OmniAuth
52
53
  raise NotImplementedError
53
54
  end
54
55
 
55
- SCHEMA_ATTRIBUTES = %w(name email nickname first_name last_name location description image phone)
56
+ SCHEMA_ATTRIBUTES = %w[name email nickname first_name last_name location description image phone]
56
57
  # A hash of as much of the standard OmniAuth schema as is stored
57
58
  # in this particular model. By default, this will call instance
58
59
  # methods for each of the attributes it needs in turn, ignoring
59
60
  # any for which `#respond_to?` is `false`.
60
61
  #
61
- # If `first_name`, `nickname`, and/or `last_name` is provided but
62
+ # If `first_name`, `nickname`, and/or `last_name` is provided but
62
63
  # `name` is not, it will be automatically calculated.
63
64
  #
64
65
  # @return [Hash] A string-keyed hash of user information.
65
66
  def info
66
- info = SCHEMA_ATTRIBUTES.inject({}) do |hash,attribute|
67
+ SCHEMA_ATTRIBUTES.each_with_object({}) do |attribute, hash|
67
68
  hash[attribute] = send(attribute) if respond_to?(attribute)
68
- hash
69
69
  end
70
- info
71
70
  end
72
71
 
73
72
  # An identifying string that must be globally unique to the
@@ -75,22 +74,23 @@ module OmniAuth
75
74
  #
76
75
  # @return [String] An identifier string unique to this identity.
77
76
  def uid
78
- if respond_to?('id')
79
- return nil if self.id.nil?
80
- self.id.to_s
77
+ if respond_to?(:id)
78
+ return nil if id.nil?
79
+
80
+ id.to_s
81
81
  else
82
- raise NotImplementedError
82
+ raise NotImplementedError
83
83
  end
84
84
  end
85
85
 
86
86
  # Used to retrieve the user-supplied authentication key (e.g. a
87
87
  # username or email). Determined using the class method of the same name,
88
- # defaults to `:email`.
88
+ # defaults to `:email`.
89
89
  #
90
90
  # @return [String] An identifying string that will be entered by
91
91
  # users upon sign in.
92
92
  def auth_key
93
- if respond_to?(self.class.auth_key)
93
+ if respond_to?(self.class.auth_key.to_sym)
94
94
  send(self.class.auth_key)
95
95
  else
96
96
  raise NotImplementedError
@@ -104,8 +104,9 @@ module OmniAuth
104
104
  # @param [String] value The value to which the auth key should be
105
105
  # set.
106
106
  def auth_key=(value)
107
- if respond_to?(self.class.auth_key + '=')
108
- send(self.class.auth_key + '=', value)
107
+ auth_key_setter = (self.class.auth_key + '=').to_sym
108
+ if respond_to?(auth_key_setter)
109
+ send(auth_key_setter, value)
109
110
  else
110
111
  raise NotImplementedError
111
112
  end
@@ -9,14 +9,15 @@ module OmniAuth
9
9
 
10
10
  self.abstract_class = true
11
11
  has_secure_password
12
-
12
+
13
13
  def self.auth_key=(key)
14
14
  super
15
- validates_uniqueness_of key, :case_sensitive => false
15
+ validates_uniqueness_of key, case_sensitive: false
16
16
  end
17
17
 
18
- def self.locate(key)
19
- where(auth_key => key).first
18
+ def self.locate(search_hash)
19
+ search_hash = search_hash.reverse_merge!('provider' => 'identity') if column_names.include?('provider')
20
+ where(search_hash).first
20
21
  end
21
22
  end
22
23
  end
@@ -1,9 +1,10 @@
1
- require 'mongo_mapper'
1
+ require 'couch_potato'
2
2
 
3
3
  module OmniAuth
4
4
  module Identity
5
5
  module Models
6
- module MongoMapper
6
+ # can not be named CouchPotato since there is a class with that name
7
+ module CouchPotatoModule
7
8
  def self.included(base)
8
9
  base.class_eval do
9
10
  include ::OmniAuth::Identity::Model
@@ -13,11 +14,11 @@ module OmniAuth
13
14
 
14
15
  def self.auth_key=(key)
15
16
  super
16
- validates_uniqueness_of key, :case_sensitive => false
17
+ validates_uniqueness_of key, case_sensitive: false
17
18
  end
18
19
 
19
- def self.locate(key)
20
- where(auth_key => key).first
20
+ def self.locate(search_hash)
21
+ where(search_hash).first
21
22
  end
22
23
  end
23
24
  end
@@ -25,4 +26,3 @@ module OmniAuth
25
26
  end
26
27
  end
27
28
  end
28
-
@@ -4,11 +4,8 @@ module OmniAuth
4
4
  module Identity
5
5
  module Models
6
6
  module Mongoid
7
-
8
7
  def self.included(base)
9
-
10
8
  base.class_eval do
11
-
12
9
  include ::OmniAuth::Identity::Model
13
10
  include ::OmniAuth::Identity::SecurePassword
14
11
 
@@ -16,17 +13,14 @@ module OmniAuth
16
13
 
17
14
  def self.auth_key=(key)
18
15
  super
19
- validates_uniqueness_of key, :case_sensitive => false
16
+ validates_uniqueness_of key, case_sensitive: false
20
17
  end
21
18
 
22
- def self.locate(key)
23
- where(auth_key => key).first
19
+ def self.locate(search_hash)
20
+ where(search_hash).first
24
21
  end
25
-
26
22
  end
27
-
28
23
  end
29
-
30
24
  end
31
25
  end
32
26
  end
@@ -9,9 +9,7 @@ module OmniAuth
9
9
  # a has_secure_password method.
10
10
  module SecurePassword
11
11
  def self.included(base)
12
- unless base.respond_to?(:has_secure_password)
13
- base.extend ClassMethods
14
- end
12
+ base.extend ClassMethods unless base.respond_to?(:has_secure_password)
15
13
  end
16
14
 
17
15
  module ClassMethods
@@ -40,7 +38,7 @@ module OmniAuth
40
38
  # User.find_by_name("david").try(:authenticate, "notright") # => nil
41
39
  # User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
42
40
  def has_secure_password
43
- attr_reader :password
41
+ attr_reader :password
44
42
 
45
43
  validates_confirmation_of :password
46
44
  validates_presence_of :password_digest
@@ -68,7 +66,7 @@ module OmniAuth
68
66
  # Encrypts the password into the password_digest attribute.
69
67
  def password=(unencrypted_password)
70
68
  @password = unencrypted_password
71
- unless unencrypted_password.blank?
69
+ if unencrypted_password && !unencrypted_password.empty?
72
70
  self.password_digest = BCrypt::Password.create(unencrypted_password)
73
71
  end
74
72
  end
@@ -1,70 +1,98 @@
1
1
  module OmniAuth
2
2
  module Strategies
3
- # The identity strategy allows you to provide simple internal
3
+ # The identity strategy allows you to provide simple internal
4
4
  # user authentication using the same process flow that you
5
5
  # use for external OmniAuth providers.
6
6
  class Identity
7
7
  include OmniAuth::Strategy
8
8
 
9
- option :fields, [:name, :email]
9
+ option :fields, %i[name email]
10
+ option :enable_login, true # See #other_phase documentation
11
+ option :on_login, nil
12
+ option :on_registration, nil
10
13
  option :on_failed_registration, nil
14
+ option :enable_registration, true
15
+ option :locate_conditions, ->(req) { { model.auth_key => req['auth_key'] } }
11
16
 
12
17
  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
18
+ if options[:on_login]
19
+ options[:on_login].call(env)
20
+ else
21
+ OmniAuth::Form.build(
22
+ title: (options[:title] || 'Identity Verification'),
23
+ url: callback_path
24
+ ) do |f|
25
+ f.text_field 'Login', 'auth_key'
26
+ f.password_field 'Password', 'password'
27
+ if options[:enable_registration]
28
+ f.html "<p align='center'><a href='#{registration_path}'>Create an Identity</a></p>"
29
+ end
30
+ end.to_response
31
+ end
21
32
  end
22
33
 
23
34
  def callback_phase
24
35
  return fail!(:invalid_credentials) unless identity
36
+
25
37
  super
26
38
  end
27
39
 
28
40
  def other_phase
29
- if on_registration_path?
41
+ if options[:enable_registration] && on_registration_path?
30
42
  if request.get?
31
43
  registration_form
32
44
  elsif request.post?
33
45
  registration_phase
46
+ else
47
+ call_app!
34
48
  end
49
+ elsif options[:enable_login] && on_request_path?
50
+ # OmniAuth, by default, disables "GET" requests for security reasons.
51
+ # This effectively disables omniauth-identity tool's login form feature.
52
+ # Because it is disabled by default, and because enabling it would desecuritize all the other
53
+ # OmniAuth strategies that may be implemented, we do not ask users to modify that setting.
54
+ # Instead we hook in here in the "other_phase", with a config setting of our own: `enable_login`
55
+ request_phase
35
56
  else
36
57
  call_app!
37
58
  end
38
59
  end
39
60
 
40
61
  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
62
+ if options[:on_registration]
63
+ options[:on_registration].call(env)
64
+ else
65
+ OmniAuth::Form.build(title: 'Register Identity') do |f|
66
+ options[:fields].each do |field|
67
+ f.text_field field.to_s.capitalize, field.to_s
68
+ end
69
+ f.password_field 'Password', 'password'
70
+ f.password_field 'Confirm Password', 'password_confirmation'
71
+ end.to_response
72
+ end
48
73
  end
49
74
 
50
75
  def registration_phase
51
- attributes = (options[:fields] + [:password, :password_confirmation]).inject({}){|h,k| h[k] = request[k.to_s]; h}
76
+ attributes = (options[:fields] + %i[password password_confirmation]).each_with_object({}) do |k, h|
77
+ h[k] = request[k.to_s]
78
+ end
79
+ if model.respond_to?(:column_names) && model.column_names.include?('provider')
80
+ attributes.reverse_merge!(provider: 'identity')
81
+ end
52
82
  @identity = model.create(attributes)
53
83
  if @identity.persisted?
54
84
  env['PATH_INFO'] = callback_path
55
85
  callback_phase
86
+ elsif options[:on_failed_registration]
87
+ env['omniauth.identity'] = @identity
88
+ options[:on_failed_registration].call(env)
56
89
  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
90
+ registration_form
63
91
  end
64
92
  end
65
93
 
66
- uid{ identity.uid }
67
- info{ identity.info }
94
+ uid { identity.uid }
95
+ info { identity.info }
68
96
 
69
97
  def registration_path
70
98
  options[:registration_path] || "#{path_prefix}/#{name}/register"
@@ -75,7 +103,13 @@ module OmniAuth
75
103
  end
76
104
 
77
105
  def identity
78
- @identity ||= model.authenticate(request['auth_key'], request['password'])
106
+ if options[:locate_conditions].is_a? Proc
107
+ conditions = instance_exec(request, &options[:locate_conditions])
108
+ conditions.to_hash
109
+ else
110
+ conditions = options[:locate_conditions].to_hash
111
+ end
112
+ @identity ||= model.authenticate(conditions, request['password'])
79
113
  end
80
114
 
81
115
  def model
@@ -1,116 +1,121 @@
1
- require 'spec_helper'
2
-
3
1
  class ExampleModel
4
2
  include OmniAuth::Identity::Model
5
3
  end
6
4
 
7
- describe OmniAuth::Identity::Model do
5
+ RSpec.describe OmniAuth::Identity::Model do
8
6
  context 'Class Methods' do
9
- subject{ ExampleModel }
7
+ subject { ExampleModel }
10
8
 
11
9
  describe '.locate' do
12
- it('should be abstract'){ lambda{ subject.locate('abc') }.should raise_error(NotImplementedError) }
10
+ it('is abstract') { expect { subject.locate('abc') }.to raise_error(NotImplementedError) }
13
11
  end
14
12
 
15
13
  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'
14
+ it 'calls locate and then authenticate' do
15
+ mocked_instance = double('ExampleModel', authenticate: 'abbadoo')
16
+ allow(subject).to receive(:locate).with('email' => 'example').and_return(mocked_instance)
17
+ expect(subject.authenticate({ 'email' => 'example' }, 'pass')).to eq('abbadoo')
18
+ end
19
+
20
+ it 'calls locate with additional scopes when provided' do
21
+ mocked_instance = double('ExampleModel', authenticate: 'abbadoo')
22
+ allow(subject).to receive(:locate).with('email' => 'example',
23
+ 'user_type' => 'admin').and_return(mocked_instance)
24
+ expect(subject.authenticate({ 'email' => 'example', 'user_type' => 'admin' }, 'pass')).to eq('abbadoo')
20
25
  end
21
26
 
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
27
+ it 'recovers gracefully if locate is nil' do
28
+ allow(subject).to receive(:locate).and_return(nil)
29
+ expect(subject.authenticate('blah', 'foo')).to be false
25
30
  end
26
31
  end
27
32
  end
28
33
 
29
34
  context 'Instance Methods' do
30
- subject{ ExampleModel.new }
35
+ subject { ExampleModel.new }
31
36
 
32
37
  describe '#authenticate' do
33
- it('should be abstract'){ lambda{ subject.authenticate('abc') }.should raise_error(NotImplementedError) }
38
+ it('is abstract') { expect { subject.authenticate('abc') }.to raise_error(NotImplementedError) }
34
39
  end
35
40
 
36
41
  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'
42
+ it 'defaults to #id' do
43
+ allow(subject).to receive(:respond_to?).with(:id).and_return(true)
44
+ allow(subject).to receive(:id).and_return 'wakka-do'
45
+ expect(subject.uid).to eq('wakka-do')
41
46
  end
42
47
 
43
- it 'should stringify it' do
44
- subject.stub!(:id).and_return 123
45
- subject.uid.should == '123'
48
+ it 'stringifies it' do
49
+ allow(subject).to receive(:id).and_return 123
50
+ expect(subject.uid).to eq('123')
46
51
  end
47
52
 
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)
53
+ it 'raises NotImplementedError if #id is not defined' do
54
+ allow(subject).to receive(:respond_to?).with(:id).and_return(false)
55
+ expect { subject.uid }.to raise_error(NotImplementedError)
51
56
  end
52
57
  end
53
58
 
54
59
  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'
60
+ it 'defaults to #email' do
61
+ allow(subject).to receive(:respond_to?).with(:email).and_return(true)
62
+ allow(subject).to receive(:email).and_return('bob@bob.com')
63
+ expect(subject.auth_key).to eq('bob@bob.com')
59
64
  end
60
65
 
61
- it 'should use the class .auth_key' do
66
+ it 'uses the class .auth_key' do
62
67
  subject.class.auth_key 'login'
63
- subject.stub!(:login).and_return 'bob'
64
- subject.auth_key.should == 'bob'
68
+ allow(subject).to receive(:login).and_return 'bob'
69
+ expect(subject.auth_key).to eq('bob')
65
70
  subject.class.auth_key nil
66
71
  end
67
72
 
68
- it 'should raise a NotImplementedError if the auth_key method is not defined' do
69
- lambda{ subject.auth_key }.should raise_error(NotImplementedError)
73
+ it 'raises a NotImplementedError if the auth_key method is not defined' do
74
+ expect { subject.auth_key }.to raise_error(NotImplementedError)
70
75
  end
71
76
  end
72
77
 
73
78
  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
-
79
+ it 'defaults to setting email' do
80
+ allow(subject).to receive(:respond_to?).with(:email=).and_return(true)
81
+ expect(subject).to receive(:email=).with 'abc'
82
+
78
83
  subject.auth_key = 'abc'
79
84
  end
80
85
 
81
- it 'should use a custom .auth_key if one is provided' do
86
+ it 'uses a custom .auth_key if one is provided' do
82
87
  subject.class.auth_key 'login'
83
- subject.should_receive(:respond_to?).with('login=').and_return(true)
84
- subject.should_receive('login=').with('abc')
88
+ allow(subject).to receive(:respond_to?).with(:login=).and_return(true)
89
+ expect(subject).to receive(:login=).with('abc')
85
90
 
86
91
  subject.auth_key = 'abc'
87
92
  end
88
93
 
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)
94
+ it 'raises a NotImplementedError if the autH_key method is not defined' do
95
+ expect { subject.auth_key = 'broken' }.to raise_error(NotImplementedError)
91
96
  end
92
97
  end
93
98
 
94
99
  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
- }
100
+ it 'includes attributes that are set' do
101
+ allow(subject).to receive(:name).and_return('Bob Bobson')
102
+ allow(subject).to receive(:nickname).and_return('bob')
103
+
104
+ expect(subject.info).to eq({
105
+ 'name' => 'Bob Bobson',
106
+ 'nickname' => 'bob'
107
+ })
103
108
  end
104
109
 
105
- it 'should automatically set name off of nickname' do
106
- subject.stub!(:nickname).and_return('bob')
110
+ it 'automaticallies set name off of nickname' do
111
+ allow(subject).to receive(:nickname).and_return('bob')
107
112
  subject.info['name'] == 'bob'
108
113
  end
109
114
 
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'
115
+ it 'does not overwrite a provided name' do
116
+ allow(subject).to receive(:name).and_return('Awesome Dude')
117
+ allow(subject).to receive(:first_name).and_return('Frank')
118
+ expect(subject.info['name']).to eq('Awesome Dude')
114
119
  end
115
120
  end
116
121
  end