ldap_lookup 0.1.7 → 2.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 +4 -4
- data/.env.example +25 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.rspec_status +46 -0
- data/Gemfile.lock +5 -3
- data/README.md +141 -15
- data/SETUP.md +117 -0
- data/config/initializers/ldap_lookup.rb.example +32 -0
- data/ldap_lookup.gemspec +4 -3
- data/ldaptest.rb +29 -6
- data/lib/ldap_lookup/version.rb +1 -1
- data/lib/ldap_lookup.rb +463 -118
- metadata +30 -14
- data/.github/workflows/codeql.yml +0 -76
- data/.github/workflows/gem-push.yml +0 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 957c311d5d68fcc94258c7f1263c2c17c4f9c2455f7043832fe5c3791ebd3cc8
|
|
4
|
+
data.tar.gz: e3029a5edf0799b9f1d5a13407639658f7360d9a1c8e4cfd663798f014008e06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32f0da915876e614ca9374d59a3045e924905b7ff2f218a4b73133d92ca718dec5afdd0f91fbcdf23052b9cddde85c37f075d8311d6a91aac539dce3cf2bfc31
|
|
7
|
+
data.tar.gz: 8d788fd099caf5b0df62e6e7eac16303b08ec18ec2b4ec734a5d3e84d0b6a730bf1bfc4b93b8ee6ec5a3af3cfd6409a46a605a8fa2b350a1fe56987d1c50a62e
|
data/.env.example
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# LDAP Configuration for Testing
|
|
2
|
+
# Copy this file to .env and fill in your actual credentials
|
|
3
|
+
# The .env file is gitignored and will not be committed
|
|
4
|
+
|
|
5
|
+
# Your UM uniqname (username)
|
|
6
|
+
LDAP_USERNAME=your_uniqname
|
|
7
|
+
|
|
8
|
+
# Your UM password
|
|
9
|
+
LDAP_PASSWORD=your_password
|
|
10
|
+
|
|
11
|
+
# Leave LDAP_USERNAME and LDAP_PASSWORD unset for anonymous binds
|
|
12
|
+
|
|
13
|
+
# Optional: Override default LDAP settings
|
|
14
|
+
# LDAP_HOST=ldap.umich.edu
|
|
15
|
+
# LDAP_PORT=389 # Use 389 for STARTTLS or 636 for LDAPS
|
|
16
|
+
# LDAP_BASE=dc=umich,dc=edu
|
|
17
|
+
# LDAP_ENCRYPTION=start_tls # Use 'start_tls' for port 389 or 'simple_tls' for port 636 (LDAPS)
|
|
18
|
+
# LDAP_DEPT_ATTRIBUTE=umichPostalAddressData
|
|
19
|
+
# LDAP_GROUP_ATTRIBUTE=umichGroupEmail
|
|
20
|
+
# LDAP_TLS_VERIFY=true # Set to false to disable cert verification (dev only)
|
|
21
|
+
# LDAP_CA_CERT=/path/to/ca-bundle.pem
|
|
22
|
+
#
|
|
23
|
+
# If STARTTLS (port 389) doesn't work, try LDAPS:
|
|
24
|
+
# LDAP_PORT=636
|
|
25
|
+
# LDAP_ENCRYPTION=simple_tls
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.rspec_status
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
example_id | status | run_time |
|
|
2
|
+
------------------------------------- | ------ | --------------- |
|
|
3
|
+
./spec/configuration_spec.rb[1:1:1:1] | passed | 0.00021 seconds |
|
|
4
|
+
./spec/configuration_spec.rb[1:1:1:2] | passed | 0.00004 seconds |
|
|
5
|
+
./spec/configuration_spec.rb[1:1:1:3] | passed | 0.00004 seconds |
|
|
6
|
+
./spec/configuration_spec.rb[1:1:2:1] | passed | 0.00149 seconds |
|
|
7
|
+
./spec/configuration_spec.rb[1:1:2:2] | passed | 0.00004 seconds |
|
|
8
|
+
./spec/configuration_spec.rb[1:1:3:1] | passed | 0.00004 seconds |
|
|
9
|
+
./spec/configuration_spec.rb[1:1:3:2] | passed | 0.00005 seconds |
|
|
10
|
+
./spec/configuration_spec.rb[1:2:1] | passed | 0.00002 seconds |
|
|
11
|
+
./spec/configuration_spec.rb[1:2:2] | passed | 0.00003 seconds |
|
|
12
|
+
./spec/configuration_spec.rb[1:3:1] | passed | 0.00003 seconds |
|
|
13
|
+
./spec/configuration_spec.rb[1:3:2] | passed | 0.00003 seconds |
|
|
14
|
+
./spec/configuration_spec.rb[1:3:3] | passed | 0.00003 seconds |
|
|
15
|
+
./spec/ldap_lookup_spec.rb[1:1:1:1] | passed | 0.63596 seconds |
|
|
16
|
+
./spec/ldap_lookup_spec.rb[1:1:2:1] | passed | 0.61562 seconds |
|
|
17
|
+
./spec/ldap_lookup_spec.rb[1:1:3:1] | passed | 0.60601 seconds |
|
|
18
|
+
./spec/ldap_lookup_spec.rb[1:2:1:1] | passed | 0.64499 seconds |
|
|
19
|
+
./spec/ldap_lookup_spec.rb[1:2:2:1] | passed | 0.6525 seconds |
|
|
20
|
+
./spec/ldap_lookup_spec.rb[1:3:1:1] | passed | 0.66369 seconds |
|
|
21
|
+
./spec/ldap_lookup_spec.rb[1:3:2:1] | passed | 1.29 seconds |
|
|
22
|
+
./spec/ldap_lookup_spec.rb[1:4:1:1] | passed | 0.64139 seconds |
|
|
23
|
+
./spec/ldap_lookup_spec.rb[1:4:2:1] | passed | 0.64128 seconds |
|
|
24
|
+
./spec/ldap_lookup_spec.rb[1:5:1:1] | passed | 1.69 seconds |
|
|
25
|
+
./spec/ldap_lookup_spec.rb[1:5:2:1] | passed | 0.75576 seconds |
|
|
26
|
+
./spec/ldap_lookup_spec.rb[1:5:3:1] | passed | 0.70272 seconds |
|
|
27
|
+
./spec/ldap_lookup_spec.rb[1:5:4:1] | passed | 0.74101 seconds |
|
|
28
|
+
./spec/ldap_lookup_spec.rb[1:6:1:1] | passed | 0.78507 seconds |
|
|
29
|
+
./spec/ldap_lookup_spec.rb[1:6:1:2] | passed | 0.77477 seconds |
|
|
30
|
+
./spec/ldap_lookup_spec.rb[1:6:1:3] | passed | 0.74474 seconds |
|
|
31
|
+
./spec/ldap_lookup_spec.rb[1:6:2:1] | passed | 0.70436 seconds |
|
|
32
|
+
./spec/ldap_lookup_spec.rb[1:7:1:1] | passed | 13.94 seconds |
|
|
33
|
+
./spec/ldap_lookup_spec.rb[1:7:1:2] | passed | 21.97 seconds |
|
|
34
|
+
./spec/ldap_lookup_spec.rb[1:7:1:3] | passed | 27.87 seconds |
|
|
35
|
+
./spec/ldap_lookup_spec.rb[1:7:2:1] | passed | 7.88 seconds |
|
|
36
|
+
./spec/ldap_lookup_spec.rb[1:8:1:1] | passed | 0.0026 seconds |
|
|
37
|
+
./spec/ldap_lookup_spec.rb[1:8:1:2] | passed | 0.00012 seconds |
|
|
38
|
+
./spec/ldap_lookup_spec.rb[1:8:1:3] | passed | 0.00009 seconds |
|
|
39
|
+
./spec/ldap_lookup_spec.rb[1:8:2:1] | passed | 0.0001 seconds |
|
|
40
|
+
./spec/ldap_lookup_spec.rb[1:8:3:1] | passed | 0.0001 seconds |
|
|
41
|
+
./spec/ldap_lookup_spec.rb[1:9:1:1] | passed | 0.0001 seconds |
|
|
42
|
+
./spec/ldap_lookup_spec.rb[1:9:2:1] | passed | 0.00009 seconds |
|
|
43
|
+
./spec/ldap_lookup_spec.rb[1:10:1:1] | passed | 0.525 seconds |
|
|
44
|
+
./spec/ldap_lookup_spec.rb[1:10:2:1] | passed | 0.46772 seconds |
|
|
45
|
+
./spec/ldap_lookup_spec.rb[1:11:1:1] | failed | 0.54755 seconds |
|
|
46
|
+
./spec/ldap_lookup_spec.rb[1:11:2:1] | passed | 0.47023 seconds |
|
data/Gemfile.lock
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
ldap_lookup (0.1.
|
|
5
|
-
net-ldap (~> 0.
|
|
4
|
+
ldap_lookup (0.1.8)
|
|
5
|
+
net-ldap (~> 0.18.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
10
|
diff-lcs (1.3)
|
|
11
|
-
|
|
11
|
+
dotenv (2.8.1)
|
|
12
|
+
net-ldap (0.18.0)
|
|
12
13
|
rake (13.0.1)
|
|
13
14
|
rspec (3.7.0)
|
|
14
15
|
rspec-core (~> 3.7.0)
|
|
@@ -29,6 +30,7 @@ PLATFORMS
|
|
|
29
30
|
|
|
30
31
|
DEPENDENCIES
|
|
31
32
|
bundler (~> 2.2.26)
|
|
33
|
+
dotenv (~> 2.8)
|
|
32
34
|
ldap_lookup!
|
|
33
35
|
rake (~> 13.0)
|
|
34
36
|
rspec (~> 3.7.0)
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# LdapLookup for Ruby [](https://badge.fury.io/rb/ldap_lookup)
|
|
2
2
|
|
|
3
3
|
### Description
|
|
4
|
-
This module is to be used for anonymous lookup of user attributes in the MCommunity service
|
|
4
|
+
This module is to be used for authenticated or anonymous lookup of user attributes in the MCommunity service provided at the University of Michigan. It supports authenticated LDAP binds with encryption as per UM IT Security requirements (effective Jan 20, 2026). It can be easily modified to use other LDAP server configurations.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ This module is to be used for anonymous lookup of user attributes in the MCommun
|
|
|
9
9
|
|
|
10
10
|
Requirements:
|
|
11
11
|
* Ruby at least 2.0.0
|
|
12
|
-
* Gem 'net-ldap' ~> '0.
|
|
12
|
+
* Gem 'net-ldap' ~> '0.17.0'
|
|
13
13
|
> *The Net::LDAP (aka net-ldap) gem before 0.16.0 for Ruby has a Missing SSL Certificate Validation.*
|
|
14
14
|
|
|
15
15
|
To try the module out:
|
|
@@ -18,13 +18,24 @@ To try the module out:
|
|
|
18
18
|
<pre>
|
|
19
19
|
LdapLookup.configuration do |config|
|
|
20
20
|
config.host = <em>< your host ></em> # "ldap.umich.edu"
|
|
21
|
-
config.port = <em>< your port ></em> # "
|
|
21
|
+
config.port = <em>< your port ></em> # "389" (default) for STARTTLS, "636" for LDAPS
|
|
22
22
|
config.base = <em>< your LDAP base ></em> # "dc=umich,dc=edu"
|
|
23
|
+
config.username = <em>< your uniqname ></em> # Your UM uniqname (e.g., "rsmoke")
|
|
24
|
+
config.password = <em>< your password ></em> # Your UM password
|
|
25
|
+
config.encryption = :start_tls # :start_tls (default, port 389) or :simple_tls (LDAPS, port 636)
|
|
23
26
|
config.dept_attribute = <em>< your dept attribute ></em> # "umichPostalAddressData"
|
|
24
27
|
config.group_attribute = <em>< your group email attribute ></em> # "umichGroupEmail"
|
|
25
28
|
end
|
|
26
29
|
</pre>
|
|
27
30
|
|
|
31
|
+
**Important:** As of January 20, 2026, UM LDAP requires:
|
|
32
|
+
- **Authenticated binds only** - Anonymous (unauthenticated) binds are not supported by UM LDAP
|
|
33
|
+
- Username and password are required for UM LDAP connections
|
|
34
|
+
- Encrypted connections (STARTTLS or LDAPS) are mandatory
|
|
35
|
+
- The gem uses LDAP "simple bind" authentication (authenticated with username/password)
|
|
36
|
+
|
|
37
|
+
The gem can also perform **anonymous binds** for LDAP servers that allow them. To use anonymous binds, leave `LDAP_USERNAME` and `LDAP_PASSWORD` unset.
|
|
38
|
+
|
|
28
39
|
3. run the ldaptest.rb script
|
|
29
40
|
```ruby
|
|
30
41
|
ruby ./ldaptest.rb
|
|
@@ -34,58 +45,173 @@ ruby ./ldaptest.rb
|
|
|
34
45
|
|
|
35
46
|
### Installation
|
|
36
47
|
|
|
48
|
+
#### Step 1: Add to Gemfile
|
|
49
|
+
|
|
37
50
|
Add this line to your application's Gemfile:
|
|
38
51
|
|
|
39
52
|
```ruby
|
|
40
53
|
gem 'ldap_lookup'
|
|
41
54
|
```
|
|
42
55
|
|
|
43
|
-
|
|
56
|
+
Then run:
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
```bash
|
|
59
|
+
bundle install
|
|
60
|
+
```
|
|
46
61
|
|
|
47
|
-
|
|
62
|
+
#### Step 2: Get LDAP Credentials
|
|
48
63
|
|
|
49
|
-
|
|
64
|
+
**For Production Applications (Recommended):**
|
|
65
|
+
Request a **service account** from your IT department. Service accounts are designed for automated applications and don't require password changes.
|
|
50
66
|
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
**For Development/Testing:**
|
|
68
|
+
You can use your personal UM uniqname and password temporarily, but switch to a service account for production.
|
|
69
|
+
|
|
70
|
+
#### Step 3: Configure the Gem
|
|
71
|
+
|
|
72
|
+
**For Rails Applications:**
|
|
73
|
+
|
|
74
|
+
Create `config/initializers/ldap_lookup.rb`:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
53
77
|
LdapLookup.configuration do |config|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
# Server Configuration (defaults work for UM LDAP)
|
|
79
|
+
config.host = ENV.fetch('LDAP_HOST', 'ldap.umich.edu')
|
|
80
|
+
config.port = ENV.fetch('LDAP_PORT', '389')
|
|
81
|
+
config.base = ENV.fetch('LDAP_BASE', 'dc=umich,dc=edu')
|
|
82
|
+
|
|
83
|
+
# Authentication (optional for anonymous binds)
|
|
84
|
+
# Leave unset to use anonymous binds (if your LDAP server allows it)
|
|
85
|
+
config.username = ENV['LDAP_USERNAME']
|
|
86
|
+
config.password = ENV['LDAP_PASSWORD']
|
|
87
|
+
|
|
88
|
+
# If using a service account with custom bind DN, uncomment and set:
|
|
89
|
+
# config.bind_dn = 'cn=service-account,ou=Service Accounts,dc=umich,dc=edu'
|
|
90
|
+
|
|
91
|
+
# Encryption - REQUIRED (defaults to STARTTLS)
|
|
92
|
+
config.encryption = ENV.fetch('LDAP_ENCRYPTION', 'start_tls').to_sym
|
|
93
|
+
# Use :simple_tls for LDAPS on port 636
|
|
94
|
+
# TLS verification (defaults to true). Set LDAP_TLS_VERIFY=false only for local testing.
|
|
95
|
+
# Optional custom CA bundle: set LDAP_CA_CERT=/path/to/ca-bundle.pem
|
|
96
|
+
|
|
97
|
+
# Optional: Attribute Configuration
|
|
98
|
+
config.dept_attribute = ENV.fetch('LDAP_DEPT_ATTRIBUTE', 'umichPostalAddressData')
|
|
99
|
+
config.group_attribute = ENV.fetch('LDAP_GROUP_ATTRIBUTE', 'umichGroupEmail')
|
|
59
100
|
end
|
|
60
|
-
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**For Non-Rails Applications:**
|
|
104
|
+
|
|
105
|
+
Configure in your application startup:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
require 'ldap_lookup'
|
|
109
|
+
|
|
110
|
+
LdapLookup.configuration do |config|
|
|
111
|
+
config.host = 'ldap.umich.edu'
|
|
112
|
+
config.base = 'dc=umich,dc=edu'
|
|
113
|
+
config.username = ENV['LDAP_USERNAME']
|
|
114
|
+
config.password = ENV['LDAP_PASSWORD']
|
|
115
|
+
config.encryption = :start_tls
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Step 4: Set Environment Variables
|
|
120
|
+
|
|
121
|
+
**Never hardcode credentials in your code!** Use environment variables (Hatchbox, Heroku, etc.):
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# In your .env file (for development)
|
|
125
|
+
LDAP_USERNAME=your_service_account_uniqname
|
|
126
|
+
LDAP_PASSWORD=your_service_account_password
|
|
127
|
+
|
|
128
|
+
# Or export in your shell
|
|
129
|
+
export LDAP_USERNAME=your_service_account_uniqname
|
|
130
|
+
export LDAP_PASSWORD=your_service_account_password
|
|
131
|
+
|
|
132
|
+
# You can also set these (all can be changed without redeploying):
|
|
133
|
+
# LDAP_HOST, LDAP_PORT, LDAP_BASE, LDAP_ENCRYPTION, LDAP_TLS_VERIFY, LDAP_CA_CERT
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**For Production:**
|
|
137
|
+
- Use your platform's secrets management (Rails credentials, AWS Secrets Manager, etc.)
|
|
138
|
+
- Never commit credentials to version control
|
|
139
|
+
- Use service accounts, not personal accounts
|
|
140
|
+
|
|
141
|
+
#### Service Account Bind DN
|
|
142
|
+
|
|
143
|
+
If your service account uses a non-standard bind DN format, you can specify it:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
config.bind_dn = 'cn=my-service-account,ou=Service Accounts,dc=umich,dc=edu'
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If `bind_dn` is not set, it defaults to: `uid=username,ou=People,base`
|
|
61
150
|
|
|
62
151
|
---
|
|
63
152
|
|
|
64
153
|
### Methods available
|
|
65
154
|
|
|
155
|
+
__uid_exist?:__ returns true if uid is in LDAP
|
|
156
|
+
```
|
|
157
|
+
LdapLookup.uid_exist?(uniqname)
|
|
158
|
+
response: true or false (boolean)
|
|
159
|
+
```
|
|
66
160
|
__get_simple_name:__ returns the Display Name
|
|
67
161
|
```
|
|
68
162
|
LdapLookup.get_simple_name(uniqname = nil)
|
|
163
|
+
response: name or "No #{attribute} found for #{uniqname}"
|
|
69
164
|
```
|
|
70
165
|
__get_dept:__ returns the users Department_name
|
|
71
166
|
```
|
|
72
167
|
LdapLookup.get_dept(uniqname = nil)
|
|
168
|
+
response: dept name or "No #{nested_attribute} found for #{uniqname}"
|
|
73
169
|
```
|
|
74
170
|
__get_email:__ returns the users email address
|
|
75
171
|
```
|
|
76
172
|
LdapLookup.get_email(uniqname = nil)
|
|
173
|
+
response: email or "No #{attribute} found for #{uniqname}"
|
|
77
174
|
```
|
|
78
175
|
__is_member_of_group?:__ returns true/false if uniqname is a member of the specified group
|
|
79
176
|
```
|
|
80
177
|
LdapLookup.is_member_of_group?(uid = nil, group_name = nil)
|
|
178
|
+
response: true or false (boolean)
|
|
81
179
|
```
|
|
82
180
|
__get_email_distribution_list:__ Returns the list of emails that are associated to a group.
|
|
83
181
|
```
|
|
84
182
|
LdapLookup.get_email_distribution_list(group_name = nil)
|
|
183
|
+
response: result_hash
|
|
85
184
|
```
|
|
86
185
|
__all_groups_for_user:__ Returns the list of groups that a user is a member of.
|
|
87
186
|
```
|
|
88
187
|
LdapLookup.all_groups_for_user(uniqname = nil)
|
|
188
|
+
response: result_array
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Running Tests
|
|
192
|
+
|
|
193
|
+
**Security Note:** Never put passwords in command line arguments. They are visible in process lists and shell history.
|
|
194
|
+
|
|
195
|
+
**Recommended: Use a .env file (most secure)**
|
|
196
|
+
1. Copy the example file: `cp .env.example .env`
|
|
197
|
+
2. Edit `.env` with your credentials:
|
|
198
|
+
```
|
|
199
|
+
LDAP_USERNAME=your_uniqname
|
|
200
|
+
LDAP_PASSWORD=your_password
|
|
201
|
+
```
|
|
202
|
+
3. Run tests: `bundle exec rspec`
|
|
203
|
+
|
|
204
|
+
**Alternative: Export environment variables**
|
|
205
|
+
```bash
|
|
206
|
+
export LDAP_USERNAME=your_uniqname
|
|
207
|
+
export LDAP_PASSWORD=your_password
|
|
208
|
+
bundle exec rspec
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Never do this (insecure):**
|
|
212
|
+
```bash
|
|
213
|
+
# ❌ DON'T: Password visible in process list
|
|
214
|
+
LDAP_PASSWORD=xxx bundle exec rspec
|
|
89
215
|
```
|
|
90
216
|
|
|
91
217
|
### Contributing
|
data/SETUP.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Quick Setup Guide for Gem Users
|
|
2
|
+
|
|
3
|
+
This guide will help you quickly set up `ldap_lookup` in your Rails application.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
1. **Get LDAP Credentials**
|
|
8
|
+
- For production: Request a service account from your IT department
|
|
9
|
+
- For development: You can temporarily use your personal uniqname/password
|
|
10
|
+
|
|
11
|
+
## Installation Steps
|
|
12
|
+
|
|
13
|
+
### 1. Add to Gemfile
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'ldap_lookup'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Run `bundle install`
|
|
20
|
+
|
|
21
|
+
### 2. Create Initializer
|
|
22
|
+
|
|
23
|
+
Copy the example initializer:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# If you have the gem source
|
|
27
|
+
cp config/initializers/ldap_lookup.rb.example config/initializers/ldap_lookup.rb
|
|
28
|
+
|
|
29
|
+
# Or create it manually
|
|
30
|
+
touch config/initializers/ldap_lookup.rb
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. Configure Credentials
|
|
34
|
+
|
|
35
|
+
Edit `config/initializers/ldap_lookup.rb`:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
LdapLookup.configuration do |config|
|
|
39
|
+
config.host = ENV.fetch('LDAP_HOST', 'ldap.umich.edu')
|
|
40
|
+
config.base = ENV.fetch('LDAP_BASE', 'dc=umich,dc=edu')
|
|
41
|
+
# Leave unset to use anonymous binds (if your LDAP server allows it)
|
|
42
|
+
config.username = ENV['LDAP_USERNAME']
|
|
43
|
+
config.password = ENV['LDAP_PASSWORD']
|
|
44
|
+
config.encryption = :start_tls
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4. Set Environment Variables
|
|
49
|
+
|
|
50
|
+
**Development (.env file):**
|
|
51
|
+
```bash
|
|
52
|
+
LDAP_USERNAME=your_service_account
|
|
53
|
+
LDAP_PASSWORD=your_password
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Production:**
|
|
57
|
+
Use your platform's secrets management:
|
|
58
|
+
- Rails: `config/credentials.yml.enc`
|
|
59
|
+
- Heroku: `heroku config:set LDAP_USERNAME=xxx`
|
|
60
|
+
- AWS: Secrets Manager
|
|
61
|
+
- etc.
|
|
62
|
+
|
|
63
|
+
### 5. Service Account Bind DN (if needed)
|
|
64
|
+
|
|
65
|
+
If your service account uses a custom bind DN format, add:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
config.bind_dn = 'cn=service-account,ou=Service Accounts,dc=umich,dc=edu'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Your IT department will provide this if it's different from the default format.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# Check if a user exists
|
|
77
|
+
LdapLookup.uid_exist?('uniqname')
|
|
78
|
+
|
|
79
|
+
# Get user's name
|
|
80
|
+
LdapLookup.get_simple_name('uniqname')
|
|
81
|
+
|
|
82
|
+
# Get user's email
|
|
83
|
+
LdapLookup.get_email('uniqname')
|
|
84
|
+
|
|
85
|
+
# Get user's department
|
|
86
|
+
LdapLookup.get_dept('uniqname')
|
|
87
|
+
|
|
88
|
+
# Check group membership
|
|
89
|
+
LdapLookup.is_member_of_group?('uniqname', 'group-name')
|
|
90
|
+
|
|
91
|
+
# Get all groups for a user
|
|
92
|
+
LdapLookup.all_groups_for_user('uniqname')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Troubleshooting
|
|
96
|
+
|
|
97
|
+
**Anonymous bind fails**
|
|
98
|
+
- Your LDAP server may require authenticated binds
|
|
99
|
+
- Set `LDAP_USERNAME` and `LDAP_PASSWORD` (service account recommended)
|
|
100
|
+
- Verify credentials are correct
|
|
101
|
+
|
|
102
|
+
**Error: Connection timeout or SSL errors**
|
|
103
|
+
- Verify `config.host` is correct
|
|
104
|
+
- Try `config.encryption = :simple_tls` with `config.port = '636'` for LDAPS
|
|
105
|
+
- Check firewall rules allow outbound LDAP connections
|
|
106
|
+
|
|
107
|
+
**Service account not working**
|
|
108
|
+
- Verify the bind DN format with your IT department
|
|
109
|
+
- Set `config.bind_dn` if your service account uses a non-standard format
|
|
110
|
+
|
|
111
|
+
## Security Reminders
|
|
112
|
+
|
|
113
|
+
- ✅ Use environment variables for credentials
|
|
114
|
+
- ✅ Use service accounts in production
|
|
115
|
+
- ✅ Never commit credentials to version control
|
|
116
|
+
- ❌ Don't hardcode passwords in code
|
|
117
|
+
- ❌ Don't use personal accounts in production
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# LDAP Lookup Configuration
|
|
2
|
+
# Copy this file to config/initializers/ldap_lookup.rb and configure with your credentials
|
|
3
|
+
#
|
|
4
|
+
# IMPORTANT: Never commit credentials to version control!
|
|
5
|
+
# Use environment variables or a secrets management system.
|
|
6
|
+
|
|
7
|
+
LdapLookup.configuration do |config|
|
|
8
|
+
# LDAP Server Configuration
|
|
9
|
+
config.host = ENV.fetch('LDAP_HOST', 'ldap.umich.edu')
|
|
10
|
+
config.port = ENV.fetch('LDAP_PORT', '389')
|
|
11
|
+
config.base = ENV.fetch('LDAP_BASE', 'dc=umich,dc=edu')
|
|
12
|
+
|
|
13
|
+
# Authentication (optional for anonymous binds)
|
|
14
|
+
# Option 1: Use a service account (recommended for production)
|
|
15
|
+
# Request a service account from your IT department
|
|
16
|
+
# Leave unset to use anonymous binds (if your LDAP server allows it)
|
|
17
|
+
config.username = ENV['LDAP_USERNAME']
|
|
18
|
+
config.password = ENV['LDAP_PASSWORD']
|
|
19
|
+
|
|
20
|
+
# Option 2: If your service account uses a custom bind DN format, specify it:
|
|
21
|
+
# config.bind_dn = 'cn=service-account,ou=Service Accounts,dc=umich,dc=edu'
|
|
22
|
+
# (If bind_dn is not set, it defaults to: uid=username,ou=People,base)
|
|
23
|
+
|
|
24
|
+
# Encryption - REQUIRED (as of Jan 20, 2026)
|
|
25
|
+
# Use :start_tls for port 389 or :simple_tls for LDAPS on port 636
|
|
26
|
+
encryption_method = ENV.fetch('LDAP_ENCRYPTION', 'start_tls').to_sym
|
|
27
|
+
config.encryption = encryption_method
|
|
28
|
+
|
|
29
|
+
# Optional: Attribute Configuration
|
|
30
|
+
config.dept_attribute = ENV.fetch('LDAP_DEPT_ATTRIBUTE', 'umichPostalAddressData')
|
|
31
|
+
config.group_attribute = ENV.fetch('LDAP_GROUP_ATTRIBUTE', 'umichGroupEmail')
|
|
32
|
+
end
|
data/ldap_lookup.gemspec
CHANGED
|
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.authors = ["Rick Smoke"]
|
|
10
10
|
spec.email = ["rsmoke@umich.edu"]
|
|
11
11
|
|
|
12
|
-
spec.summary = %q{
|
|
13
|
-
spec.description = %q{This
|
|
12
|
+
spec.summary = %q{Authenticated LDAP lookup for MCommunity user attributes at University of Michigan.}
|
|
13
|
+
spec.description = %q{This gem provides authenticated LDAP lookups for user attributes in the MCommunity service at the University of Michigan. It supports encrypted connections (STARTTLS/LDAPS) and service accounts as required by UM IT Security (effective Jan 20, 2026). Can be easily modified for other LDAP server configurations.}
|
|
14
14
|
spec.homepage = "https://github.com/rsmoke/ldap_lookup.git"
|
|
15
15
|
spec.license = "MIT"
|
|
16
16
|
|
|
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 2.2.26"
|
|
25
25
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.7.0"
|
|
27
|
-
spec.
|
|
27
|
+
spec.add_development_dependency "dotenv", "~> 2.8"
|
|
28
|
+
spec.add_dependency 'net-ldap', '~> 0.18.0'
|
|
28
29
|
end
|
data/ldaptest.rb
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
# Load .env file if it exists (for local development)
|
|
4
|
+
begin
|
|
5
|
+
require 'dotenv/load'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
# dotenv not available, will use environment variables or fallbacks
|
|
8
|
+
end
|
|
9
|
+
|
|
3
10
|
require_relative "lib/ldap_lookup"
|
|
4
11
|
|
|
5
12
|
class Ldaptest
|
|
@@ -7,10 +14,20 @@ class Ldaptest
|
|
|
7
14
|
|
|
8
15
|
############## CONFIGURATION BLOCK ###################
|
|
9
16
|
LdapLookup.configuration do |config|
|
|
10
|
-
config.host = "ldap.umich.edu"
|
|
11
|
-
config.
|
|
12
|
-
config.
|
|
13
|
-
|
|
17
|
+
config.host = ENV['LDAP_HOST'] || "ldap.umich.edu"
|
|
18
|
+
config.port = ENV['LDAP_PORT'] || "389"
|
|
19
|
+
config.base = ENV['LDAP_BASE'] || "dc=umich,dc=edu"
|
|
20
|
+
# Leave username/password unset for anonymous binds
|
|
21
|
+
config.username = ENV['LDAP_USERNAME']
|
|
22
|
+
config.password = ENV['LDAP_PASSWORD']
|
|
23
|
+
# Read encryption from ENV, default to start_tls
|
|
24
|
+
encryption_str = ENV['LDAP_ENCRYPTION'] || 'start_tls'
|
|
25
|
+
config.encryption = encryption_str.to_sym
|
|
26
|
+
config.dept_attribute = ENV['LDAP_DEPT_ATTRIBUTE'] || "umichPostalAddressData"
|
|
27
|
+
config.group_attribute = ENV['LDAP_GROUP_ATTRIBUTE'] || "umichGroupEmail"
|
|
28
|
+
# Enable LDAP debug logging in this test runner
|
|
29
|
+
debug_str = ENV['LDAP_DEBUG']
|
|
30
|
+
config.debug = debug_str ? debug_str.to_s.downcase == 'true' : true
|
|
14
31
|
end
|
|
15
32
|
#######################################################
|
|
16
33
|
|
|
@@ -32,7 +49,7 @@ class Ldaptest
|
|
|
32
49
|
end
|
|
33
50
|
|
|
34
51
|
def result_box(answer)
|
|
35
|
-
print "\e[2J\e[f"
|
|
52
|
+
# print "\e[2J\e[f"
|
|
36
53
|
2.times { puts " " }
|
|
37
54
|
puts "Your Results"
|
|
38
55
|
puts "======================================================"
|
|
@@ -56,6 +73,7 @@ class Ldaptest
|
|
|
56
73
|
puts "2: set new group_uid"
|
|
57
74
|
puts "+++++++++++++++++++++++++"
|
|
58
75
|
puts "3: get users full name"
|
|
76
|
+
puts "33: check if uid exists"
|
|
59
77
|
puts "4: get users department"
|
|
60
78
|
puts "5: get users email"
|
|
61
79
|
puts "55: get all groups a user is a member of"
|
|
@@ -64,23 +82,28 @@ class Ldaptest
|
|
|
64
82
|
puts "7: check if uid is member of a group"
|
|
65
83
|
puts "+++++++++++++++++++++++++"
|
|
66
84
|
puts "8: what time is it?"
|
|
85
|
+
puts "99: test LDAP connection (diagnostic)"
|
|
67
86
|
puts "0: exit"
|
|
87
|
+
puts ""
|
|
88
|
+
print "Enter a number: "
|
|
68
89
|
|
|
69
90
|
case gets.chomp.to_i
|
|
70
91
|
when 1 then result_box(reset_uid)
|
|
71
92
|
when 2 then result_box(reset_group_uid)
|
|
72
93
|
when 3 then result_box(LdapLookup.get_simple_name(@uid))
|
|
94
|
+
when 33 then result_box(LdapLookup.uid_exist?(@uid))
|
|
73
95
|
when 4 then result_box(LdapLookup.get_dept(@uid))
|
|
74
96
|
when 5 then result_box(LdapLookup.get_email(@uid))
|
|
75
97
|
when 55 then result_box(LdapLookup.all_groups_for_user(@uid))
|
|
76
98
|
when 6 then result_box(LdapLookup.get_email_distribution_list(@group_uid))
|
|
77
99
|
when 7 then result_box(LdapLookup.is_member_of_group?(@uid, @group_uid))
|
|
78
100
|
when 8 then result_box(timestamp)
|
|
101
|
+
when 99 then result_box(LdapLookup.test_connection.inspect)
|
|
79
102
|
when 0 then puts "you chose exit!"
|
|
80
103
|
throw(:done)
|
|
81
104
|
else
|
|
82
105
|
print "\e[2J\e[f"
|
|
83
|
-
puts "====> Please type 1,2,3,4,5,55,6,7,8 or 0 only"
|
|
106
|
+
puts "====> Please type 1,2,3,33,4,5,55,6,7,8 or 0 only"
|
|
84
107
|
2.times { puts " " }
|
|
85
108
|
end
|
|
86
109
|
end
|
data/lib/ldap_lookup/version.rb
CHANGED