ons-ldap 1.0.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.
- checksums.yaml +7 -0
- data/README.md +38 -0
- data/lib/ons-ldap.rb +1 -0
- data/lib/ons-ldap/ldap_connection.rb +106 -0
- data/lib/ons-ldap/version.rb +8 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b2d412ce6348a1a3eefe1ff3bc96a1fac4f980f9
|
4
|
+
data.tar.gz: d77ef02808888a498f15a5970861faac220e958e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 811b492711c0b3a4cb97a54717fc2ad486daf1acbc33f65cfc7436a56aad8943063ec9df5d27b6546d119d44b761f9124256115ae75bd14df17f50b9ad1472c2
|
7
|
+
data.tar.gz: 43fb9137d557753022d7203e4314fccf2ad1b5aef4a9de4a621f5fce4f360deba45ea2076f72b31e2e3337c1c408b67eebe5bc21965851a7bd17ec4797ff348c
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# ONS LDAP RubyGem
|
2
|
+
Thin wrapper around the [net-ldap](https://rubygems.org/gems/net-ldap) RubyGem. Contains a simple `LDAPConnection` class that can be used to authenticate against an LDAP directory.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
```
|
7
|
+
gem install ons-ldap
|
8
|
+
```
|
9
|
+
|
10
|
+
## Example
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require 'ons-ldap'
|
14
|
+
|
15
|
+
host = 'localhost ' # LDAP server host
|
16
|
+
port = '398' # LDAP server port
|
17
|
+
base = 'dc=example,dc=com' # LDAP tree base
|
18
|
+
|
19
|
+
# Hash of LDAP group names.
|
20
|
+
groups = { admins: 'admins', users: 'users' }
|
21
|
+
|
22
|
+
ldap_connection = LDAPConnection.new(host, port, base, groups, logger)
|
23
|
+
user_entry = ldap_connection.authenticate('johntopley', 'password')
|
24
|
+
|
25
|
+
user_entry.user_id #=> 'johntopley'
|
26
|
+
user_entry.display_name #=> 'John Topley'
|
27
|
+
user_entry.token # 2FA token, stored in LDAP's employeeNumber field for expediency
|
28
|
+
user_entry.groups #=> ['admins', 'users']
|
29
|
+
```
|
30
|
+
|
31
|
+
## Testing
|
32
|
+
|
33
|
+
```
|
34
|
+
rake test
|
35
|
+
```
|
36
|
+
|
37
|
+
## Copyright
|
38
|
+
Copyright (C) 2016 Crown Copyright (Office for National Statistics)
|
data/lib/ons-ldap.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'ons-ldap/ldap_connection'
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'net/ldap'
|
2
|
+
|
3
|
+
UserEntry = Struct.new(:user_id, :display_name, :token, :groups)
|
4
|
+
|
5
|
+
class LDAPConnection
|
6
|
+
|
7
|
+
# Class instance variables.
|
8
|
+
class << self
|
9
|
+
attr_accessor :host
|
10
|
+
attr_accessor :port
|
11
|
+
attr_accessor :base
|
12
|
+
attr_accessor :groups
|
13
|
+
attr_accessor :logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(host, port, base, groups, logger)
|
17
|
+
self.class.host = host
|
18
|
+
self.class.port = port.to_i
|
19
|
+
self.class.base = base
|
20
|
+
self.class.groups = groups
|
21
|
+
self.class.logger = logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def authenticate(username, password)
|
25
|
+
user_entry = nil
|
26
|
+
|
27
|
+
# Have to use the username DN format below for the bind operation to succeed.
|
28
|
+
auth = { method: :simple, username: "uid=#{username},ou=Users,#{self.class.base}", password: password }
|
29
|
+
|
30
|
+
Net::LDAP.open(host: self.class.host, port: self.class.port, base: self.class.base, auth: auth) do |ldap|
|
31
|
+
unless ldap.bind
|
32
|
+
result = ldap.get_operation_result
|
33
|
+
self.class.logger.error "LDAP authentication failed for '#{username}': #{result.message} (#{result.code})"
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
self.class.logger.info "LDAP authentication succeeded for '#{username}'"
|
38
|
+
user_entry = entry_for(username, ldap) || nil
|
39
|
+
|
40
|
+
# The user must be a member of at least the "<zone>-users" group for authentication to be considered successful.
|
41
|
+
users_group = self.class.groups['users']
|
42
|
+
unless group_member?(users_group, username, ldap)
|
43
|
+
self.class.logger.error "LDAP authentication failed: '#{username}' is not a member of the '#{users_group}' group"
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
user_entry.groups = groups_for(username, ldap)
|
48
|
+
end
|
49
|
+
|
50
|
+
user_entry
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Returns the LDAP directory entry for a user.
|
56
|
+
def entry_for(username, ldap)
|
57
|
+
filter = Net::LDAP::Filter.construct("uid=#{username}")
|
58
|
+
attributes = %w(uid displayName employeeNumber) # employeeNumber is used to store the 2FA token.
|
59
|
+
user_entry = nil
|
60
|
+
|
61
|
+
succeeded = ldap.search(filter: filter, attributes: attributes, return_result: false) do |entry|
|
62
|
+
user_entry = UserEntry.new(entry.uid.first, entry.displayName.first, entry.employeeNumber.first)
|
63
|
+
end
|
64
|
+
|
65
|
+
unless succeeded
|
66
|
+
result = ldap.get_operation_result
|
67
|
+
self.class.logger.error "Error searching the LDAP directory using filter '#{filter}': #{result.message} (#{result.code})"
|
68
|
+
end
|
69
|
+
|
70
|
+
user_entry
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the LDAP groups a user belongs to.
|
74
|
+
def groups_for(username, ldap)
|
75
|
+
filter = Net::LDAP::Filter.construct("(&(objectClass=posixGroup)(memberUid=#{username}))")
|
76
|
+
attributes = %w(cn)
|
77
|
+
groups = []
|
78
|
+
succeeded = ldap.search(filter: filter, attributes: attributes, return_result: false) do |entry|
|
79
|
+
groups << entry[:cn].first
|
80
|
+
end
|
81
|
+
unless succeeded
|
82
|
+
result = ldap.get_operation_result
|
83
|
+
self.class.logger.error "Error searching the LDAP directory using filter '#{filter}': #{result.message} (#{result.code})"
|
84
|
+
end
|
85
|
+
groups
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns whether a user is a member of a group.
|
89
|
+
def group_member?(group, username, ldap)
|
90
|
+
self.class.logger.info "group: '#{group}'"
|
91
|
+
filter = Net::LDAP::Filter.construct("(&(cn=#{group})(memberUid=#{username}))")
|
92
|
+
attributes = %w(cn)
|
93
|
+
group_cn = nil
|
94
|
+
|
95
|
+
succeeded = ldap.search(filter: filter, attributes: attributes, return_result: false) do |entry|
|
96
|
+
group_cn = entry.cn.first
|
97
|
+
end
|
98
|
+
|
99
|
+
unless succeeded
|
100
|
+
result = ldap.get_operation_result
|
101
|
+
self.class.logger.error "Error searching the LDAP directory using filter '#{filter}': #{result.message} (#{result.code})"
|
102
|
+
end
|
103
|
+
|
104
|
+
group_cn == group
|
105
|
+
end
|
106
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ons-ldap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Topley
|
8
|
+
- Philip Sharland
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-08-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: net-ldap
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0.11'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.11'
|
34
|
+
description: Simple class for authenticating against an LDAP directory.
|
35
|
+
email:
|
36
|
+
- john.topley@ons.gov.uk
|
37
|
+
- philip.sharland@ons.gov.uk
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- README.md
|
43
|
+
- lib/ons-ldap.rb
|
44
|
+
- lib/ons-ldap/ldap_connection.rb
|
45
|
+
- lib/ons-ldap/version.rb
|
46
|
+
homepage: https://github.com/ONSdigital/ldap-rubygem
|
47
|
+
licenses:
|
48
|
+
- Crown Copyright (Office for National Statistics)
|
49
|
+
metadata: {}
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 2.6.6
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: LDAP connection class
|
70
|
+
test_files: []
|