omniauth-identity2 2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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