lex-kerberos 0.1.2 → 0.1.3
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f1d192f07e0352efa5eac1726291cab685501a0ef58d3ec63977ac9e803e607
|
|
4
|
+
data.tar.gz: e7c528d4073e5d21ba399799323784a079b3574054c71db6df756c55c940b3e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99e260e9bd9c16e1426eb2fb16227984bae0ef8ac450481fd7cc949423e25b930fc90ae377a89e80dca2c373db18b200b840d77da22ee34814541bd401255f15
|
|
7
|
+
data.tar.gz: b4ab6bb59a514d3c380ea5da4e47e05dd53a22d2da209e7829ec7d5012442231c93baad21406369fbc4636c0b21c69072914712e8be116c306d21f59efeecd9f
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.1.3] - 2026-03-19
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `Hooks::Negotiate` hook class for Kerberos SPNEGO authentication via expanded hooks system
|
|
9
|
+
- `Runners::Authenticate#negotiate` method handling full HTTP Negotiate flow with custom response
|
|
10
|
+
- RBAC role mapping and JWT token issuance (guarded, requires LegionIO framework)
|
|
11
|
+
- Negotiate endpoint now routes through `Ingress.run` for RBAC and audit support
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Kerberos negotiate endpoint moves from hardcoded `/api/auth/negotiate` in LegionIO to `/api/hooks/lex/kerberos/negotiate`
|
|
15
|
+
|
|
5
16
|
## [0.1.2] - 2026-03-18
|
|
6
17
|
|
|
7
18
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -113,7 +113,7 @@ Optional framework dependencies (guarded with `defined?`, not in gemspec):
|
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
115
|
bundle install
|
|
116
|
-
bundle exec rspec # 43 specs, 91.67% coverage
|
|
116
|
+
bundle exec rspec # 43 specs across 8 spec files, 91.67% coverage
|
|
117
117
|
bundle exec rubocop # Clean
|
|
118
118
|
```
|
|
119
119
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Kerberos
|
|
6
|
+
module Hooks
|
|
7
|
+
class Negotiate < Legion::Extensions::Hooks::Base
|
|
8
|
+
def route(_headers, _payload)
|
|
9
|
+
:negotiate
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def runner_class
|
|
13
|
+
'Legion::Extensions::Kerberos::Runners::Authenticate'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -32,6 +32,20 @@ module Legion
|
|
|
32
32
|
{ result: build_result(spnego: spnego, groups: groups, ldap_error: ldap_error, profile: profile) }
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def negotiate(headers: {}, **)
|
|
36
|
+
auth_header = headers['HTTP_AUTHORIZATION']
|
|
37
|
+
unless auth_header&.match?(/\ANegotiate\s+/i)
|
|
38
|
+
return negotiate_error('negotiate_required', 'Negotiate token required')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
auth_result = negotiate_authenticate(auth_header.sub(/\ANegotiate\s+/i, ''))
|
|
42
|
+
unless auth_result&.dig(:success)
|
|
43
|
+
return negotiate_error('kerberos_auth_failed', 'Kerberos authentication failed')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
negotiate_success(auth_result)
|
|
47
|
+
end
|
|
48
|
+
|
|
35
49
|
private
|
|
36
50
|
|
|
37
51
|
def resolve_groups(ldap:, cfg:, username:)
|
|
@@ -53,6 +67,66 @@ module Legion
|
|
|
53
67
|
ldap_error: ldap_error, **spnego_fields, **profile }.compact
|
|
54
68
|
end
|
|
55
69
|
|
|
70
|
+
def negotiate_authenticate(token)
|
|
71
|
+
Client.new.authenticate(token: token)
|
|
72
|
+
rescue StandardError
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def negotiate_error(code, message)
|
|
77
|
+
body = negotiate_json({ error: { code: code, message: message },
|
|
78
|
+
meta: { timestamp: Time.now.utc.iso8601 } })
|
|
79
|
+
{
|
|
80
|
+
result: { error: code },
|
|
81
|
+
response: { status: 401, content_type: 'application/json',
|
|
82
|
+
headers: { 'WWW-Authenticate' => 'Negotiate' }, body: body }
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def negotiate_success(auth_result)
|
|
87
|
+
profile = auth_result.slice(:first_name, :last_name, :email, :display_name)
|
|
88
|
+
token, roles = issue_negotiate_token(auth_result, profile)
|
|
89
|
+
data = { token: token, principal: auth_result[:principal],
|
|
90
|
+
roles: roles, auth_method: 'kerberos', **profile }.compact
|
|
91
|
+
negotiate_success_response(auth_result, data)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def negotiate_success_response(auth_result, data)
|
|
95
|
+
hdrs = ({ 'WWW-Authenticate' => "Negotiate #{auth_result[:output_token]}" } if auth_result[:output_token])
|
|
96
|
+
body = negotiate_json({ data: data, meta: { timestamp: Time.now.utc.iso8601 } })
|
|
97
|
+
{ result: data,
|
|
98
|
+
response: { status: 200, content_type: 'application/json',
|
|
99
|
+
headers: hdrs, body: body }.compact }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def issue_negotiate_token(auth_result, profile)
|
|
103
|
+
return [nil, []] unless defined?(Legion::Rbac::KerberosClaimsMapper) && defined?(Legion::API::Token)
|
|
104
|
+
|
|
105
|
+
mapped = map_negotiate_claims(auth_result, profile)
|
|
106
|
+
display = mapped[:display_name] || mapped[:first_name]
|
|
107
|
+
token = Legion::API::Token.issue_human_token(
|
|
108
|
+
msid: mapped[:sub], name: display, roles: mapped[:roles], ttl: 28_800
|
|
109
|
+
)
|
|
110
|
+
[token, mapped[:roles]]
|
|
111
|
+
rescue StandardError
|
|
112
|
+
[nil, []]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def map_negotiate_claims(auth_result, profile)
|
|
116
|
+
role_map = defined?(Legion::Settings) ? (Legion::Settings.dig(:kerberos, :role_map) || {}) : {}
|
|
117
|
+
Legion::Rbac::KerberosClaimsMapper.map_with_fallback(
|
|
118
|
+
principal: auth_result[:principal], groups: auth_result[:groups] || [],
|
|
119
|
+
role_map: role_map, **profile
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def negotiate_json(hash)
|
|
124
|
+
return Legion::JSON.dump(hash) if defined?(Legion::JSON)
|
|
125
|
+
|
|
126
|
+
require 'json'
|
|
127
|
+
::JSON.generate(hash)
|
|
128
|
+
end
|
|
129
|
+
|
|
56
130
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
57
131
|
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
58
132
|
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.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -59,6 +59,7 @@ files:
|
|
|
59
59
|
- lib/legion/extensions/kerberos/helpers/keytab.rb
|
|
60
60
|
- lib/legion/extensions/kerberos/helpers/ldap.rb
|
|
61
61
|
- lib/legion/extensions/kerberos/helpers/spnego.rb
|
|
62
|
+
- lib/legion/extensions/kerberos/hooks/negotiate.rb
|
|
62
63
|
- lib/legion/extensions/kerberos/runners/authenticate.rb
|
|
63
64
|
- lib/legion/extensions/kerberos/version.rb
|
|
64
65
|
homepage: https://github.com/LegionIO/lex-kerberos
|