omniauth-identity2 2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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(search_hash)
19
+ where(search_hash).first
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ require 'couch_potato'
2
+
3
+ module OmniAuth
4
+ module Identity
5
+ module Models
6
+ # can not be named CouchPotato since there is a class with that name
7
+ module CouchPotatoModule
8
+
9
+ def self.included(base)
10
+
11
+ base.class_eval do
12
+
13
+ include ::OmniAuth::Identity::Model
14
+ include ::OmniAuth::Identity::SecurePassword
15
+
16
+ has_secure_password
17
+
18
+ def self.auth_key=(key)
19
+ super
20
+ validates_uniqueness_of key, :case_sensitive => false
21
+ end
22
+
23
+ def self.locate(search_hash)
24
+ where(search_hash).first
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ require 'dm-core'
2
+ require 'dm-validations'
3
+
4
+ module OmniAuth
5
+ module Identity
6
+ module Models
7
+ module DataMapper
8
+ def self.included(base)
9
+ base.class_eval do
10
+ include OmniAuth::Identity::Model
11
+ include OmniAuth::Identity::SecurePassword
12
+
13
+ # http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-persisted-3F
14
+ # http://rubydoc.info/github/mongoid/mongoid/master/Mongoid/State#persisted%3F-instance_method
15
+ alias persisted? valid?
16
+
17
+ has_secure_password
18
+
19
+ def self.auth_key=(key)
20
+ super
21
+ validates_uniqueness_of :key
22
+ end
23
+
24
+ def self.locate(search_hash)
25
+ all(search_hash).first
26
+ end
27
+ end
28
+ end
29
+ end # DataMapper
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ require 'mongoid'
2
+
3
+ module OmniAuth
4
+ module Identity
5
+ module Models
6
+ module Mongoid
7
+
8
+ def self.included(base)
9
+
10
+ base.class_eval do
11
+
12
+ include ::OmniAuth::Identity::Model
13
+ include ::OmniAuth::Identity::SecurePassword
14
+
15
+ has_secure_password
16
+
17
+ def self.auth_key=(key)
18
+ super
19
+ validates_uniqueness_of key, :case_sensitive => false
20
+ end
21
+
22
+ def self.locate(search_hash)
23
+ where(search_hash).first
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ 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
+ if unencrypted_password && !unencrypted_password.empty?
72
+ self.password_digest = BCrypt::Password.create(unencrypted_password)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,103 @@
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_login, nil
11
+ option :on_registration, nil
12
+ option :on_failed_registration, nil
13
+ option :locate_conditions, lambda{|req| {model.auth_key => req['auth_key']} }
14
+
15
+ def request_phase
16
+ if options[:on_login]
17
+ options[:on_login].call(self.env)
18
+ else
19
+ OmniAuth::Form.build(
20
+ :title => (options[:title] || "Identity Verification"),
21
+ :url => callback_path
22
+ ) do |f|
23
+ f.text_field 'Login', 'auth_key'
24
+ f.password_field 'Password', 'password'
25
+ f.html "<p align='center'><a href='#{registration_path}'>Create an Identity</a></p>"
26
+ end.to_response
27
+ end
28
+ end
29
+
30
+ def callback_phase
31
+ return fail!(:invalid_credentials) unless identity
32
+ super
33
+ end
34
+
35
+ def other_phase
36
+ if on_registration_path?
37
+ if request.get?
38
+ registration_form
39
+ elsif request.post?
40
+ registration_phase
41
+ end
42
+ else
43
+ call_app!
44
+ end
45
+ end
46
+
47
+ def registration_form
48
+ if options[:on_registration]
49
+ options[:on_registration].call(self.env)
50
+ else
51
+ OmniAuth::Form.build(:title => 'Register Identity') do |f|
52
+ options[:fields].each do |field|
53
+ f.text_field field.to_s.capitalize, field.to_s
54
+ end
55
+ f.password_field 'Password', 'password'
56
+ f.password_field 'Confirm Password', 'password_confirmation'
57
+ end.to_response
58
+ end
59
+ end
60
+
61
+ def registration_phase
62
+ attributes = (options[:fields] + [:password, :password_confirmation]).inject({}){|h,k| h[k] = request[k.to_s]; h}
63
+ @identity = model.create(attributes)
64
+ if @identity.persisted?
65
+ env['PATH_INFO'] = callback_path
66
+ callback_phase
67
+ else
68
+ if options[:on_failed_registration]
69
+ self.env['omniauth.identity'] = @identity
70
+ options[:on_failed_registration].call(self.env)
71
+ else
72
+ registration_form
73
+ end
74
+ end
75
+ end
76
+
77
+ uid{ identity.uid }
78
+ info{ identity.info }
79
+
80
+ def registration_path
81
+ options[:registration_path] || "#{path_prefix}/#{name}/register"
82
+ end
83
+
84
+ def on_registration_path?
85
+ on_path?(registration_path)
86
+ end
87
+
88
+ def identity
89
+ if options.locate_conditions.is_a? Proc
90
+ conditions = instance_exec(request, &options.locate_conditions)
91
+ conditions.to_hash
92
+ else
93
+ conditions = options.locate_conditions.to_hash
94
+ end
95
+ @identity ||= model.authenticate(conditions, request['password'] )
96
+ end
97
+
98
+ def model
99
+ options[:model] || ::Identity
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,33 @@
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'
6
+ gem.add_runtime_dependency 'bcrypt'
7
+
8
+ gem.add_development_dependency 'maruku'
9
+ gem.add_development_dependency 'simplecov'
10
+ gem.add_development_dependency 'rack-test'
11
+ gem.add_development_dependency 'rake'
12
+ gem.add_development_dependency 'rspec', '~> 3'
13
+ gem.add_development_dependency 'activerecord'
14
+ gem.add_development_dependency 'mongoid'
15
+ gem.add_development_dependency 'datamapper'
16
+ gem.add_development_dependency 'bson_ext'
17
+ gem.add_development_dependency 'byebug'
18
+ gem.add_development_dependency 'couch_potato'
19
+
20
+ gem.name = 'omniauth-identity2'
21
+ gem.version = OmniAuth::Identity::VERSION
22
+ gem.description = %q{Internal authentication handlers for OmniAuth. A modern version of the Omniauth Identity strategy.}
23
+ gem.summary = gem.description
24
+ gem.email = ['andy.roberts.uk@gmail.com']
25
+ gem.homepage = 'https://github.com/Jellybooks/omniauth-identity2'
26
+ gem.authors = ['Andrew Roberts', 'Michael Bleigh']
27
+ gem.license = 'MIT'
28
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
29
+ gem.files = `git ls-files`.split("\n")
30
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
31
+ gem.require_paths = ['lib']
32
+ gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
33
+ end
@@ -0,0 +1,121 @@
1
+ class ExampleModel
2
+ include OmniAuth::Identity::Model
3
+ end
4
+
5
+ describe OmniAuth::Identity::Model do
6
+ context 'Class Methods' do
7
+ subject{ ExampleModel }
8
+
9
+ describe '.locate' do
10
+ it('should be abstract'){ expect{ subject.locate('abc') }.to raise_error(NotImplementedError) }
11
+ end
12
+
13
+ describe '.authenticate' do
14
+ it 'should call 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 'should call locate with additional scopes when provided' do
21
+ mocked_instance = double('ExampleModel', :authenticate => 'abbadoo')
22
+ allow(subject).to receive(:locate).with('email' => 'example', 'user_type' => 'admin').and_return(mocked_instance)
23
+ expect(subject.authenticate({'email' => 'example', 'user_type' => 'admin'}, 'pass')).to eq('abbadoo')
24
+ end
25
+
26
+ it 'should recover gracefully if locate is nil' do
27
+ allow(subject).to receive(:locate).and_return(nil)
28
+ expect(subject.authenticate('blah','foo')).to be false
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'Instance Methods' do
34
+ subject{ ExampleModel.new }
35
+
36
+ describe '#authenticate' do
37
+ it('should be abstract'){ expect{ subject.authenticate('abc') }.to raise_error(NotImplementedError) }
38
+ end
39
+
40
+ describe '#uid' do
41
+ it 'should default to #id' do
42
+ allow(subject).to receive(:respond_to?).with(:id).and_return(true)
43
+ allow(subject).to receive(:id).and_return 'wakka-do'
44
+ expect(subject.uid).to eq('wakka-do')
45
+ end
46
+
47
+ it 'should stringify it' do
48
+ allow(subject).to receive(:id).and_return 123
49
+ expect(subject.uid).to eq('123')
50
+ end
51
+
52
+ it 'should raise NotImplementedError if #id is not defined' do
53
+ allow(subject).to receive(:respond_to?).with(:id).and_return(false)
54
+ expect{ subject.uid }.to raise_error(NotImplementedError)
55
+ end
56
+ end
57
+
58
+ describe '#auth_key' do
59
+ it 'should default to #email' do
60
+ allow(subject).to receive(:respond_to?).with(:email).and_return(true)
61
+ allow(subject).to receive(:email).and_return('bob@bob.com')
62
+ expect(subject.auth_key).to eq('bob@bob.com')
63
+ end
64
+
65
+ it 'should use the class .auth_key' do
66
+ subject.class.auth_key 'login'
67
+ allow(subject).to receive(:login).and_return 'bob'
68
+ expect(subject.auth_key).to eq('bob')
69
+ subject.class.auth_key nil
70
+ end
71
+
72
+ it 'should raise a NotImplementedError if the auth_key method is not defined' do
73
+ expect{ subject.auth_key }.to raise_error(NotImplementedError)
74
+ end
75
+ end
76
+
77
+ describe '#auth_key=' do
78
+ it 'should default to setting email' do
79
+ allow(subject).to receive(:respond_to?).with(:email=).and_return(true)
80
+ expect(subject).to receive(:email=).with 'abc'
81
+
82
+ subject.auth_key = 'abc'
83
+ end
84
+
85
+ it 'should use a custom .auth_key if one is provided' do
86
+ subject.class.auth_key 'login'
87
+ allow(subject).to receive(:respond_to?).with(:login=).and_return(true)
88
+ expect(subject).to receive(:login=).with('abc')
89
+
90
+ subject.auth_key = 'abc'
91
+ end
92
+
93
+ it 'should raise a NotImplementedError if the autH_key method is not defined' do
94
+ expect{ subject.auth_key = 'broken' }.to raise_error(NotImplementedError)
95
+ end
96
+ end
97
+
98
+ describe '#info' do
99
+ it 'should include attributes that are set' do
100
+ allow(subject).to receive(:name).and_return('Bob Bobson')
101
+ allow(subject).to receive(:nickname).and_return('bob')
102
+
103
+ expect(subject.info).to eq({
104
+ 'name' => 'Bob Bobson',
105
+ 'nickname' => 'bob'
106
+ })
107
+ end
108
+
109
+ it 'should automatically set name off of nickname' do
110
+ allow(subject).to receive(:nickname).and_return('bob')
111
+ subject.info['name'] == 'bob'
112
+ end
113
+
114
+ it 'should not overwrite a provided name' do
115
+ allow(subject).to receive(:name).and_return('Awesome Dude')
116
+ allow(subject).to receive(:first_name).and_return('Frank')
117
+ expect(subject.info['name']).to eq('Awesome Dude')
118
+ end
119
+ end
120
+ end
121
+ end