incline_ldap 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 37bfcb6c7e5e530989ba3bde1834af9b36979de3
4
+ data.tar.gz: 0b48332d9b5e0257ae0c3370800f9d0d2ebe9a4d
5
+ SHA512:
6
+ metadata.gz: 508731fc38ff8024680871c2e243f3831ad8e544900ec47e6bbd0ed392e4abd0f373d5ea4d470a757b3a49edc16db6695c3fb8743c66ab87676520357bc242ee
7
+ data.tar.gz: 993ac522658e546b77db483254ce86d90f9da3ba7879d60183c008efc489399dbbf38c9b5c9e3f6b59f6b3e0ba55e4792fe97aada91c25f13904db14d9deffe3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in incline_ldap.gemspec
4
+ gemspec
5
+
6
+ gem 'eventmachine', group: :test
7
+
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Beau Barker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # InclineLdap
2
+
3
+ The incline_ldap gem adds LDAP authentication to an [Incline](https://github.com/barkerest/incline) application.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'incline_ldap'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install incline_ldap
20
+
21
+ ## Usage
22
+
23
+
24
+
25
+ ## Contributing
26
+
27
+ Bug reports and pull requests are welcome on GitHub at https://github.com/barkerest/incline_ldap.
28
+
29
+
30
+ ## License
31
+
32
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
33
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "incline_ldap"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,193 @@
1
+ require 'net-ldap'
2
+ require 'incline'
3
+ require 'securerandom'
4
+
5
+ module InclineLdap
6
+
7
+ ##
8
+ # Defines an engine used to authenticate a user against an LDAP provider.
9
+ class AuthEngine < ::Incline::AuthEngineBase
10
+
11
+ ##
12
+ # An error raised when attempting to establish a connection to the LDAP provider.
13
+ ConnectionError = Class.new(StandardError)
14
+
15
+ ##
16
+ # Raised when a configuration value is invalid.
17
+ InvalidConfiguration = Class.new(ConnectionError)
18
+
19
+ ##
20
+ # Raised when the configuration looks good but we are still unable to connect.
21
+ BindError = Class.new(ConnectionError)
22
+
23
+ ##
24
+ # Creates a new LDAP authentication engine.
25
+ #
26
+ # Valid options:
27
+ # host::
28
+ # The LDAP host name or IP address. (required)
29
+ # port::
30
+ # The port to connect to (defaults to 389 for non-ssl and 636 for ssl).
31
+ # ssl::
32
+ # Should SSL be used for the connection (recommended, default is true).
33
+ # base_dn::
34
+ # The base DN to search within when looking for user accounts. (required)
35
+ # browse_user::
36
+ # A user to log in as when looking for user accounts. (required)
37
+ # browse_password::
38
+ # The password for the browse_user.
39
+ # email_attribute::
40
+ # The attribute to use when looking for user accounts (default is 'mail').
41
+ # auto_create::
42
+ # If true, users are automatically created when they successfully authenticate against the LDAP provider for
43
+ # the first time.
44
+ # auto_activate::
45
+ # If this and auto_create are both true, then newly created users will be activated. If auto_create is false,
46
+ # this option has no effect. If this is false and auto_create is true, newly created users will need to
47
+ # activate their accounts as if they had signed up.
48
+ #
49
+ def initialize(options = {})
50
+ @options = {
51
+ ssl: true,
52
+ email_attribute: 'mail'
53
+ }.merge(options || {})
54
+
55
+ @options[:port] = @options[:port].to_s.to_i unless @options[:port].is_a?(::Integer)
56
+
57
+ if @options[:port] == 0
58
+ @options[:port] = (@options[:ssl] ? 636 : 389)
59
+ end
60
+
61
+ raise InvalidConfiguration, "Missing value for 'host' parameter." if @options[:host].blank?
62
+ raise InvalidConfiguration, "The value for 'port' must be between 1 and 65535." unless (1..65535).include?(@options[:port])
63
+ raise InvalidConfiguration, "Missing value for 'base_dn' parameter." if @options[:base_dn].blank?
64
+ raise InvalidConfiguration, "Missing value for 'email_attribute' parameter." if @options[:email_attribute].blank?
65
+ raise InvalidConfiguration, "Missing value for 'browse_user' parameter." if @options[:browse_user].blank?
66
+
67
+ ldap_opt = {
68
+ host: @options[:host],
69
+ port: @options[:port],
70
+ base: @options[:base_dn],
71
+ auth: {
72
+ method: :simple,
73
+ username: @options[:browse_user],
74
+ password: @options[:browse_password]
75
+ }
76
+ }
77
+
78
+ if @options[:ssl]
79
+ @options[:ssl] = @options[:ssl].to_sym if @options[:ssl].is_a?(::String)
80
+
81
+ unless [:simple_tls, :start_tls].include?(@options[:ssl])
82
+ @options[:ssl] =
83
+ if @options[:port] == 389
84
+ :start_tls
85
+ else
86
+ :simple_tls
87
+ end
88
+ end
89
+
90
+ ldap_opt[:encryption] = { method: @options[:ssl] }
91
+ end
92
+
93
+ ::Incline::Log::debug "Creating new LDAP connection to #{@options[:host]}:#{@options[:port]}..."
94
+ @ldap = Net::LDAP.new(ldap_opt)
95
+
96
+ ::Incline::Log::debug 'Binding to LDAP server...'
97
+ raise BindError, "Failed to connect to #{@options[:host]}:#{@options[:port]}." unless @ldap.bind
98
+
99
+ ::Incline::Log::info "Connected to LDAP host #{@options[:host]}:#{@options[:port]}."
100
+ end
101
+
102
+ ##
103
+ # Gets the host name or IP address for this LDAP authenticator.
104
+ def host
105
+ @options[:host]
106
+ end
107
+
108
+ ##
109
+ # Gets the host port for this LDAP authenticator.
110
+ def port
111
+ @options[:port]
112
+ end
113
+
114
+ ##
115
+ # Gets the SSL method for this LDAP authenticator.
116
+ def ssl
117
+ @options[:ssl]
118
+ end
119
+
120
+ ##
121
+ # Gets the Base DN for this LDAP authenticator.
122
+ def base_dn
123
+ @options[:base_dn]
124
+ end
125
+
126
+ ##
127
+ # Gets the email attribute name for this LDAP authenticator.
128
+ def email_attribute
129
+ @options[:email_attribute]
130
+ end
131
+
132
+ ##
133
+ # Authenticates a user against an LDAP provider.
134
+ def authenticate(email, password, client_ip)
135
+ ldap_filter = "(&(objectClass=person)(#{@options[:email_attribute]}=#{email}))"
136
+
137
+ reset_ldap!
138
+
139
+ search_result = @ldap.search(filter: ldap_filter)
140
+
141
+ if search_result && search_result.count == 1
142
+ search_result = search_result.first
143
+
144
+ user = ::Incline::User.find_by(email: email)
145
+
146
+ if @options[:auto_create] && user.nil?
147
+ rpwd = ::SecureRandom.urlsafe_base64(48)
148
+ ::Incline::Recaptcha::pause_for do
149
+ user =
150
+ ::Incline::User.create(
151
+ email: email,
152
+ password: rpwd,
153
+ password_confirmation: rpwd,
154
+ name: search_result[:displayName]&.first || search_result[:name]&.first || search_result[:cn]&.first,
155
+ recaptcha: 'none',
156
+ enabled: true,
157
+ activated: !!@options[:auto_activate],
158
+ activated_at: (@options[:auto_activate] ? Time.now : nil)
159
+ )
160
+ unless @options[:auto_activate]
161
+ user.send_activation_email client_ip
162
+ end
163
+ end
164
+ end
165
+
166
+ if user
167
+ unless user.enabled?
168
+ add_failure_to user, '(LDAP) account disabled', client_ip
169
+ return nil
170
+ end
171
+ entry = @ldap.bind_as(filter: ldap_filter, password: password)
172
+ if entry && entry.count == 1
173
+ add_success_to user, '(LDAP)', client_ip
174
+ return user
175
+ else
176
+ add_failure_to user, '(LDAP) invalid password', client_ip
177
+ return nil
178
+ end
179
+ end
180
+ end
181
+ add_failure_to email, '(LDAP) invalid email', client_ip
182
+ nil
183
+ end
184
+
185
+ private
186
+
187
+ def reset_ldap!
188
+ @ldap.auth @options[:browse_user], @options[:browse_password]
189
+ @ldap.bind
190
+ end
191
+
192
+ end
193
+ end
@@ -0,0 +1,3 @@
1
+ module InclineLdap
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'incline'
2
+ require 'incline_ldap/version'
3
+ require 'incline_ldap/auth_engine'
4
+
5
+ ##
6
+ # Extends the Incline gem with LDAP support.
7
+ module InclineLdap
8
+
9
+
10
+ end
@@ -0,0 +1,58 @@
1
+ require 'test_helper'
2
+
3
+ class InclineLdapTest < Minitest::Test
4
+
5
+ def setup
6
+ @config = {
7
+ host: 'ldap.forumsys.com',
8
+ port: 389,
9
+ ssl: false,
10
+ base_dn: 'dc=example,dc=com',
11
+ browse_user: 'cn=read-only-admin,dc=example,dc=com',
12
+ browse_password: 'password',
13
+ auto_create: true,
14
+ auto_activate: true
15
+ }
16
+ end
17
+
18
+ def test_should_connect_and_authenticate
19
+ aa = InclineLdap::AuthEngine.new(@config)
20
+ euler = 'euler@ldap.forumsys.com'
21
+ einstein = 'einstein@ldap.forumsys.com'
22
+ frank = 'frankenstein@ldap.forumsys.com'
23
+
24
+ assert aa
25
+ # should not be able to login as "Euler" with incorrect password.
26
+ assert_nil aa.authenticate(euler, 'wrong', '127.0.0.1')
27
+ # should be able to login as "Euler" with correct password.
28
+ assert aa.authenticate(euler, 'password', '127.0.0.1')
29
+ # should not be able to login as "Frankenstein" at all.
30
+ assert_nil aa.authenticate(frank, 'password', '127.0.0.1')
31
+ # should be able to login as 'Einstein'.
32
+ assert aa.authenticate(einstein, 'password', '127.0.0.1')
33
+ end
34
+
35
+ def test_should_raise_error_for_invalid_config
36
+ [
37
+ [ :host, nil ],
38
+ [ :host, '' ],
39
+ [ :host, ' ' ],
40
+ [ :port, -1 ],
41
+ [ :port, 65536 ],
42
+ [ :base_dn, nil ],
43
+ [ :base_dn, '' ],
44
+ [ :base_dn, ' ' ],
45
+ [ :browse_user, nil ],
46
+ [ :browse_user, '' ],
47
+ [ :browse_user, ' ' ],
48
+ [ :browse_password, 'invalid-password' ]
49
+ ].each do |(k,v)|
50
+ assert_raises(InclineLdap::AuthEngine::ConnectionError) do
51
+ InclineLdap::AuthEngine.new(@config.merge({ k => v }))
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+
58
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'incline_ldap'
3
+
4
+ require 'minitest/autorun'
5
+
6
+ Dir.mkdir('tmp') unless Dir.exist?('tmp')
7
+
8
+ # Connect to a temporary database.
9
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', pool: 5, database: 'tmp/incline_ldap_test.sqlite3'
10
+
11
+ # Execute migrations.
12
+ Incline.migrate!
13
+
14
+ require 'byebug'
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: incline_ldap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Beau Barker
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: incline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-ldap
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.16'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.3.13
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.3.13
97
+ description:
98
+ email:
99
+ - beau@barkerest.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - lib/incline_ldap.rb
113
+ - lib/incline_ldap/auth_engine.rb
114
+ - lib/incline_ldap/version.rb
115
+ - test/incline_ldap_test.rb
116
+ - test/test_helper.rb
117
+ homepage: https://github.com/barkerest/incline_ldap/
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: 2.3.0
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.5.2
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Adds LDAP authentication support to Incline.
141
+ test_files: []