lex-kerberos 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15ea37e6d645fc9dd2e3c6cb8efb0cde73d4b4fef19cf5d86107e31a82a31bc2
4
- data.tar.gz: 9269c50bf9e6ef8b21f7f36ba515576404d522f3893c60903543b8de73564ab7
3
+ metadata.gz: 69c3dd5c8ef68a25cc39f06b412e3b9f07854e02643ef44bf2f1082965c72d62
4
+ data.tar.gz: 0d1b20b4dfc12d31e21a9a00c14be7f76f15ece54a2d8fdb3fcc9703ee76cc46
5
5
  SHA512:
6
- metadata.gz: e3cb6c6e00bee1659282fde1f92f718e748f145eb270f50d5d014cd203e584ec74cf01bd229404bf5cc340cd1a415d38cff0d0475f91d9afa7628e1f8573f9af
7
- data.tar.gz: 6911ada268f1b63dadf7a909be989c49474448be7a7e081682f8956f4d70c85d6881294b3958c6d1c47d9f581badf8449f591a4ff1e11ab6b77dda620dbf45be
6
+ metadata.gz: c7663b961a7468ccb52e6133b9d12ab57cb47c086cfe2f6264f3a35f3697340a5725415aeb5d115838d44bb0195b4d8e5cb25ef05b681a3680561d57734ffcae
7
+ data.tar.gz: 27de46aa7005ba6d43a57b0460f2436fecf15a4851a0cccc6f5198efbd82c906d4b9b17f37c8d571a538ba4514ff594a5d5bca614c518050bf735bcd1dca00fb
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.1.2] - 2026-03-18
6
+
7
+ ### Added
8
+ - Organizational LDAP attributes: `title`, `department`, `company`, `co`, `c`, `l`, `st`, `cn`, `whenCreated`
9
+ - `PROFILE_MAP` constant maps LDAP attribute names to readable Ruby symbols
10
+
11
+ ### Changed
12
+ - `USER_ATTRIBUTES` expanded to include organizational fields
13
+ - `extract_profile` uses `PROFILE_MAP` for declarative attribute mapping
14
+
15
+ ## [0.1.1] - 2026-03-18
16
+
17
+ ### Added
18
+ - LDAP user profile fetching: `givenName`, `sn`, `mail`, `displayName` attributes returned from `lookup_groups`
19
+ - `first_name`, `last_name`, `email`, `display_name` fields in authenticate runner result
20
+
21
+ ### Changed
22
+ - LDAP search now fetches `USER_ATTRIBUTES` constant (memberOf + profile fields) instead of just group attribute
23
+
5
24
  ## [0.1.0] - 2026-03-17
6
25
 
7
26
  ### Added
data/CLAUDE.md CHANGED
@@ -10,7 +10,7 @@ Legion Extension that provides Kerberos/SPNEGO authentication. Validates SPNEGO
10
10
 
11
11
  **GitHub**: https://github.com/LegionIO/lex-kerberos
12
12
  **License**: MIT
13
- **Version**: 0.1.0
13
+ **Version**: 0.1.2
14
14
 
15
15
  ## Architecture
16
16
 
@@ -34,13 +34,13 @@ Legion::Extensions::Kerberos
34
34
  |------|---------|
35
35
  | `lib/legion/extensions/kerberos.rb` | Entry point, requires all helpers/runners/actors, extends Core |
36
36
  | `lib/legion/extensions/kerberos/helpers/spnego.rb` | GSSAPI token acceptance via `gssapi` gem; `accept_spnego_token`, `extract_username`, `extract_realm` |
37
- | `lib/legion/extensions/kerberos/helpers/ldap.rb` | LDAP group lookup via `net-ldap`; `lookup_groups` with configurable filter/attribute |
37
+ | `lib/legion/extensions/kerberos/helpers/ldap.rb` | LDAP group lookup + profile via `net-ldap`; `lookup_groups` returns groups + org attributes via `PROFILE_MAP` |
38
38
  | `lib/legion/extensions/kerberos/helpers/keytab.rb` | Multi-source keytab resolution; vault:// URI, file path, Base64 blob; writes to `~/.legionio/kerberos/legion.keytab` |
39
39
  | `lib/legion/extensions/kerberos/helpers/client.rb` | `DEFAULTS` constant and `settings` method that merges with `Legion::Settings[:kerberos]` |
40
40
  | `lib/legion/extensions/kerberos/runners/authenticate.rb` | `validate_spnego` runner: orchestrates keytab resolve → SPNEGO accept → optional LDAP lookup |
41
41
  | `lib/legion/extensions/kerberos/actors/keytab_refresh.rb` | Hourly actor that calls `resolve_keytab` to re-cache from Vault; `run_now? false` (no immediate run at boot) |
42
42
  | `lib/legion/extensions/kerberos/client.rb` | Standalone `Client` class with `authenticate(token:)` and `resolve_groups(username:)` |
43
- | `lib/legion/extensions/kerberos/version.rb` | `VERSION = '0.1.0'` |
43
+ | `lib/legion/extensions/kerberos/version.rb` | `VERSION = '0.1.2'` |
44
44
 
45
45
  ## Key Patterns
46
46
 
data/README.md CHANGED
@@ -86,9 +86,12 @@ result = client.authenticate(token: token)
86
86
  # => { success: true, principal: "user@EXAMPLE.COM", username: "user",
87
87
  # realm: "EXAMPLE.COM", output_token: "...", ... }
88
88
 
89
- # Resolve LDAP groups for a username
89
+ # Resolve LDAP groups and profile for a username
90
90
  groups = client.resolve_groups(username: 'user')
91
- # => { success: true, groups: ["CN=Domain Users,..."], username: "user" }
91
+ # => { success: true, groups: ["CN=Domain Users,..."], username: "user",
92
+ # first_name: "Jane", last_name: "Doe", email: "jane.doe@example.com",
93
+ # title: "Senior Engineer", department: "Platform Engineering",
94
+ # company: "Acme Corp", city: "Minneapolis", state: "MN", country: "USA" }
92
95
  ```
93
96
 
94
97
  ### Using helpers directly
@@ -7,6 +7,17 @@ module Legion
7
7
  module Kerberos
8
8
  module Helpers
9
9
  module Ldap
10
+ USER_ATTRIBUTES = %w[
11
+ memberOf givenName sn mail displayName cn
12
+ title department company co c l st whenCreated
13
+ ].freeze
14
+
15
+ PROFILE_MAP = {
16
+ first_name: :givenname, last_name: :sn, email: :mail, display_name: :displayname,
17
+ cn: :cn, title: :title, department: :department, company: :company,
18
+ country: :co, country_code: :c, city: :l, state: :st, ad_created_at: :whencreated
19
+ }.freeze
20
+
10
21
  def lookup_groups(username:, host:, base_dn:, bind_dn:, bind_password:,
11
22
  port: 636, encryption: :simple_tls,
12
23
  user_filter: '(sAMAccountName=%<username>s)',
@@ -15,9 +26,8 @@ module Legion
15
26
  bind_dn: bind_dn, bind_password: bind_password)
16
27
  return { success: false, error: 'LDAP bind failed' } unless ldap.bind
17
28
 
18
- groups = search_groups(ldap: ldap, username: username, base_dn: base_dn,
19
- user_filter: user_filter, group_attribute: group_attribute)
20
- { success: true, groups: groups, username: username }
29
+ search_user(ldap: ldap, username: username, base_dn: base_dn,
30
+ user_filter: user_filter, group_attribute: group_attribute)
21
31
  rescue Net::LDAP::Error => e
22
32
  { success: false, error: "LDAP error: #{e.message}" }
23
33
  end
@@ -32,13 +42,19 @@ module Legion
32
42
  )
33
43
  end
34
44
 
35
- def search_groups(ldap:, username:, base_dn:, user_filter:, group_attribute:)
45
+ def search_user(ldap:, username:, base_dn:, user_filter:, group_attribute:)
36
46
  filter = Net::LDAP::Filter.construct(format(user_filter, username: username))
37
47
  groups = []
38
- ldap.search(base: base_dn, filter: filter, attributes: [group_attribute]) do |entry|
48
+ profile = {}
49
+ ldap.search(base: base_dn, filter: filter, attributes: USER_ATTRIBUTES) do |entry|
39
50
  groups.concat(Array(entry[group_attribute]).map(&:to_s))
51
+ profile = extract_profile(entry)
40
52
  end
41
- groups
53
+ { success: true, groups: groups, username: username, **profile }
54
+ end
55
+
56
+ def extract_profile(entry)
57
+ PROFILE_MAP.transform_values { |attr| entry[attr]&.first&.to_s }.compact
42
58
  end
43
59
  end
44
60
  end
@@ -27,37 +27,30 @@ module Legion
27
27
  service_principal: service_principal)
28
28
  return { result: spnego } unless spnego[:success]
29
29
 
30
- groups, ldap_error = resolve_groups(ldap: ldap, cfg: s, username: spnego[:username])
30
+ groups, ldap_error, profile = resolve_groups(ldap: ldap, cfg: s, username: spnego[:username])
31
31
 
32
- { result: build_result(spnego: spnego, groups: groups, ldap_error: ldap_error) }
32
+ { result: build_result(spnego: spnego, groups: groups, ldap_error: ldap_error, profile: profile) }
33
33
  end
34
34
 
35
35
  private
36
36
 
37
37
  def resolve_groups(ldap:, cfg:, username:)
38
38
  ldap_opts = ldap || cfg[:ldap] || {}
39
- if ldap_opts[:host]
40
- groups_result = lookup_groups(username: username, **ldap_opts)
41
- groups = groups_result[:success] ? groups_result[:groups] : []
42
- ldap_error = groups_result[:success] ? nil : groups_result[:error]
39
+ return [[], nil, {}] unless ldap_opts[:host]
40
+
41
+ result = lookup_groups(username: username, **ldap_opts)
42
+ if result[:success]
43
+ profile = result.slice(:first_name, :last_name, :email, :display_name)
44
+ [result[:groups], nil, profile]
43
45
  else
44
- groups = []
45
- ldap_error = nil
46
+ [[], result[:error], {}]
46
47
  end
47
- [groups, ldap_error]
48
48
  end
49
49
 
50
- def build_result(spnego:, groups:, ldap_error:)
51
- {
52
- success: true,
53
- principal: spnego[:principal],
54
- username: spnego[:username],
55
- realm: spnego[:realm],
56
- groups: groups,
57
- output_token: spnego[:output_token],
58
- auth_method: 'kerberos',
59
- ldap_error: ldap_error
60
- }.compact
50
+ def build_result(spnego:, groups:, ldap_error:, profile: {})
51
+ spnego_fields = spnego.slice(:principal, :username, :realm, :output_token)
52
+ { success: true, groups: groups, auth_method: 'kerberos',
53
+ ldap_error: ldap_error, **spnego_fields, **profile }.compact
61
54
  end
62
55
 
63
56
  include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Kerberos
6
- VERSION = '0.1.0'
6
+ VERSION = '0.1.2'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-kerberos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -50,7 +50,6 @@ files:
50
50
  - CHANGELOG.md
51
51
  - CLAUDE.md
52
52
  - Gemfile
53
- - Gemfile.lock
54
53
  - README.md
55
54
  - lex-kerberos.gemspec
56
55
  - lib/legion/extensions/kerberos.rb
data/Gemfile.lock DELETED
@@ -1,159 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- lex-kerberos (0.1.0)
5
- gssapi (~> 1.3)
6
- net-ldap (~> 0.19)
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- addressable (2.8.9)
12
- public_suffix (>= 2.0.2, < 8.0)
13
- ast (2.4.3)
14
- base64 (0.3.0)
15
- bigdecimal (4.0.1)
16
- diff-lcs (1.6.2)
17
- docile (1.4.1)
18
- ffi (1.17.3)
19
- ffi (1.17.3-aarch64-linux-gnu)
20
- ffi (1.17.3-aarch64-linux-musl)
21
- ffi (1.17.3-arm-linux-gnu)
22
- ffi (1.17.3-arm-linux-musl)
23
- ffi (1.17.3-arm64-darwin)
24
- ffi (1.17.3-x86-linux-gnu)
25
- ffi (1.17.3-x86-linux-musl)
26
- ffi (1.17.3-x86_64-darwin)
27
- ffi (1.17.3-x86_64-linux-gnu)
28
- ffi (1.17.3-x86_64-linux-musl)
29
- gssapi (1.3.1)
30
- ffi (>= 1.0.1)
31
- json (2.19.1)
32
- json-schema (6.2.0)
33
- addressable (~> 2.8)
34
- bigdecimal (>= 3.1, < 5)
35
- language_server-protocol (3.17.0.5)
36
- lint_roller (1.1.0)
37
- mcp (0.8.0)
38
- json-schema (>= 4.1)
39
- net-ldap (0.20.0)
40
- base64
41
- ostruct
42
- ostruct (0.6.3)
43
- parallel (1.27.0)
44
- parser (3.3.10.2)
45
- ast (~> 2.4.1)
46
- racc
47
- prism (1.9.0)
48
- public_suffix (7.0.5)
49
- racc (1.8.1)
50
- rainbow (3.1.1)
51
- regexp_parser (2.11.3)
52
- rspec (3.13.2)
53
- rspec-core (~> 3.13.0)
54
- rspec-expectations (~> 3.13.0)
55
- rspec-mocks (~> 3.13.0)
56
- rspec-core (3.13.6)
57
- rspec-support (~> 3.13.0)
58
- rspec-expectations (3.13.5)
59
- diff-lcs (>= 1.2.0, < 2.0)
60
- rspec-support (~> 3.13.0)
61
- rspec-mocks (3.13.8)
62
- diff-lcs (>= 1.2.0, < 2.0)
63
- rspec-support (~> 3.13.0)
64
- rspec-support (3.13.7)
65
- rubocop (1.85.1)
66
- json (~> 2.3)
67
- language_server-protocol (~> 3.17.0.2)
68
- lint_roller (~> 1.1.0)
69
- mcp (~> 0.6)
70
- parallel (~> 1.10)
71
- parser (>= 3.3.0.2)
72
- rainbow (>= 2.2.2, < 4.0)
73
- regexp_parser (>= 2.9.3, < 3.0)
74
- rubocop-ast (>= 1.49.0, < 2.0)
75
- ruby-progressbar (~> 1.7)
76
- unicode-display_width (>= 2.4.0, < 4.0)
77
- rubocop-ast (1.49.1)
78
- parser (>= 3.3.7.2)
79
- prism (~> 1.7)
80
- ruby-progressbar (1.13.0)
81
- simplecov (0.22.0)
82
- docile (~> 1.1)
83
- simplecov-html (~> 0.11)
84
- simplecov_json_formatter (~> 0.1)
85
- simplecov-html (0.13.2)
86
- simplecov_json_formatter (0.1.4)
87
- unicode-display_width (3.2.0)
88
- unicode-emoji (~> 4.1)
89
- unicode-emoji (4.2.0)
90
-
91
- PLATFORMS
92
- aarch64-linux-gnu
93
- aarch64-linux-musl
94
- arm-linux-gnu
95
- arm-linux-musl
96
- arm64-darwin
97
- ruby
98
- x86-linux-gnu
99
- x86-linux-musl
100
- x86_64-darwin
101
- x86_64-linux-gnu
102
- x86_64-linux-musl
103
-
104
- DEPENDENCIES
105
- lex-kerberos!
106
- rspec (~> 3.13)
107
- rubocop (~> 1.75)
108
- simplecov (~> 0.22)
109
-
110
- CHECKSUMS
111
- addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485
112
- ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
113
- base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
114
- bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
115
- diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
116
- docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
117
- ffi (1.17.3) sha256=0e9f39f7bb3934f77ad6feab49662be77e87eedcdeb2a3f5c0234c2938563d4c
118
- ffi (1.17.3-aarch64-linux-gnu) sha256=28ad573df26560f0aedd8a90c3371279a0b2bd0b4e834b16a2baa10bd7a97068
119
- ffi (1.17.3-aarch64-linux-musl) sha256=020b33b76775b1abacc3b7d86b287cef3251f66d747092deec592c7f5df764b2
120
- ffi (1.17.3-arm-linux-gnu) sha256=5bd4cea83b68b5ec0037f99c57d5ce2dd5aa438f35decc5ef68a7d085c785668
121
- ffi (1.17.3-arm-linux-musl) sha256=0d7626bb96265f9af78afa33e267d71cfef9d9a8eb8f5525344f8da6c7d76053
122
- ffi (1.17.3-arm64-darwin) sha256=0c690555d4cee17a7f07c04d59df39b2fba74ec440b19da1f685c6579bb0717f
123
- ffi (1.17.3-x86-linux-gnu) sha256=868a88fcaf5186c3a46b7c7c2b2c34550e1e61a405670ab23f5b6c9971529089
124
- ffi (1.17.3-x86-linux-musl) sha256=f0286aa6ef40605cf586e61406c446de34397b85dbb08cc99fdaddaef8343945
125
- ffi (1.17.3-x86_64-darwin) sha256=1f211811eb5cfaa25998322cdd92ab104bfbd26d1c4c08471599c511f2c00bb5
126
- ffi (1.17.3-x86_64-linux-gnu) sha256=3746b01f677aae7b16dc1acb7cb3cc17b3e35bdae7676a3f568153fb0e2c887f
127
- ffi (1.17.3-x86_64-linux-musl) sha256=086b221c3a68320b7564066f46fed23449a44f7a1935f1fe5a245bd89d9aea56
128
- gssapi (1.3.1) sha256=c51cf30842ee39bd93ce7fc33e20405ff8a04cda9dec6092071b61258284aee1
129
- json (2.19.1) sha256=dd94fdc59e48bff85913829a32350b3148156bc4fd2a95a2568a78b11344082d
130
- json-schema (6.2.0) sha256=e8bff46ed845a22c1ab2bd0d7eccf831c01fe23bb3920caa4c74db4306813666
131
- language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
132
- lex-kerberos (0.1.0)
133
- lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
134
- mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
135
- net-ldap (0.20.0) sha256=b2080b350753a9ac4930869ded8e61a1d2151c01e03b0bf07b4675cbd9ce5372
136
- ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
137
- parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
138
- parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357
139
- prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
140
- public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
141
- racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
142
- rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
143
- regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
144
- rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
145
- rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
146
- rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
147
- rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
148
- rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
149
- rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77
150
- rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
151
- ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
152
- simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
153
- simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
154
- simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
155
- unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
156
- unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
157
-
158
- BUNDLED WITH
159
- 4.0.8