legion-crypt 1.5.2 → 1.5.4

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: 01cec7ac57c7d5448b5fba4237fdccc9ac8099966a4a546e95dd4888d838604f
4
- data.tar.gz: ac81d602c9fc34493aeabe3fdeadd12f01fb36f8c248ac44687166fd24d9a48d
3
+ metadata.gz: 63ba23115939920477655378eaff3adacb4f37dac754cffc7e888a9eebc8ab08
4
+ data.tar.gz: 88960246bf06ae1e228529958d9e431693a9fa8e8279234ae1498c130bf1490d
5
5
  SHA512:
6
- metadata.gz: dea3247dbaf00a49507ededcfdd72e9e3545485cbcec511657d2e2c602e03f2230c3c34e93108ae14ede71aa7a768a36573c3f852595444f2f6ed118a351bd8c
7
- data.tar.gz: 011ac0f235a69f7655ab1237d2a87faf3d343de20ef1ffe1e9576b5a555e9c0e5d46dfc6d77dd137851835850e3877783c70f62a216c3dd1a4af9c3e080e9ed6
6
+ metadata.gz: abf411468507856a834cc3e06df344eaecc5deebcb552a77d4cfd30ac1c091506f8b284eea79660d3dc109f2e622816c32871d4c4eda047a51ec3a9dbb689467
7
+ data.tar.gz: 557b616f75af3857df71a6530ea4a7e26680163fc2274e3a09ecfb2a12de628cc9bf64b90fb8e527c8ae43b1ac741a070b3f68d281591369ab460e2e348bd4e8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Legion::Crypt
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [1.5.4] - 2026-04-06
6
+
7
+ ### Added
8
+ - `JWT.issue_identity_token` — convenience method wrapping `JWT.issue` with identity claims from `Identity::Process` (Wire Format Phase 3); accepts `issuer:` kwarg (defaults to `'legion'`) passed through to `JWT.issue`; normalizes and rejects conflicting string-keyed extra claims before merging
9
+
10
+ ## [1.5.3] - 2026-04-06
11
+
12
+ ### Added
13
+ - `JwksClient.prefetch!(url)` — fire-and-forget JWKS key fetch in background thread
14
+ - `JwksClient.start_background_refresh!(url, interval:)` — `Concurrent::TimerTask` for hourly key refresh
15
+ - `JwksClient.stop_background_refresh!` — stops background refresh timer task
16
+ - `bootstrap_lease_ttl: 300` in vault defaults (5-minute TTL for bootstrap credentials)
17
+
18
+ ### Changed
19
+ - `JwksClient.clear_cache` now also stops any running background refresh task
20
+
3
21
  ## [1.5.2] - 2026-04-03
4
22
 
5
23
  ### Fixed
data/legion-crypt.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  'rubygems_mfa_required' => 'true'
26
26
  }
27
27
 
28
+ spec.add_dependency 'concurrent-ruby', '~> 1.3'
28
29
  spec.add_dependency 'ed25519', '~> 1.3'
29
30
  spec.add_dependency 'jwt', '>= 2.7'
30
31
  spec.add_dependency 'legion-logging', '>= 1.5.0'
@@ -6,6 +6,7 @@ require 'json'
6
6
  require 'openssl'
7
7
  require 'jwt'
8
8
  require 'legion/logging/helper'
9
+ require 'concurrent'
9
10
 
10
11
  module Legion
11
12
  module Crypt
@@ -56,7 +57,33 @@ module Legion
56
57
  raise Legion::Crypt::JWT::InvalidTokenError, "signing key not found: #{kid}"
57
58
  end
58
59
 
60
+ def prefetch!(jwks_url)
61
+ Thread.new do
62
+ fetch_keys(jwks_url)
63
+ rescue StandardError => e
64
+ log.debug "JWKS prefetch failed for #{jwks_url}: #{e.message}" if respond_to?(:log)
65
+ end
66
+ end
67
+
68
+ def start_background_refresh!(jwks_url, interval: CACHE_TTL)
69
+ stop_background_refresh!
70
+
71
+ @refresh_task = Concurrent::TimerTask.new(execution_interval: interval, run_now: false) do
72
+ fetch_keys(jwks_url)
73
+ rescue StandardError => e
74
+ log.debug "JWKS background refresh failed: #{e.message}" if respond_to?(:log)
75
+ end
76
+ @refresh_task.execute
77
+ log.info "JWKS background refresh started (interval=#{interval}s)" if respond_to?(:log)
78
+ end
79
+
80
+ def stop_background_refresh!
81
+ @refresh_task&.shutdown
82
+ @refresh_task = nil
83
+ end
84
+
59
85
  def clear_cache
86
+ stop_background_refresh!
60
87
  @cache_mutex.synchronize { @cache = {} }
61
88
  @locks_mutex.synchronize { @locks = {} }
62
89
  log.info 'JWKS cache cleared'
@@ -36,6 +36,29 @@ module Legion
36
36
  raise
37
37
  end
38
38
 
39
+ def self.issue_identity_token(signing_key:, extra_claims: {}, algorithm: 'HS256', ttl: 3600, issuer: 'legion')
40
+ unless defined?(Legion::Identity::Process) && Legion::Identity::Process.resolved?
41
+ raise ArgumentError,
42
+ 'Identity::Process not resolved'
43
+ end
44
+
45
+ identity = Legion::Identity::Process.identity_hash
46
+ identity_fields = {
47
+ sub: identity[:canonical_name],
48
+ principal_id: identity[:id],
49
+ canonical_name: identity[:canonical_name],
50
+ kind: identity[:kind].to_s,
51
+ mode: identity[:mode].to_s,
52
+ groups: (identity[:groups] || [])[0, 50]
53
+ }
54
+ normalized_extra_claims = symbolize_keys(extra_claims || {}).reject do |key, _value|
55
+ identity_fields.key?(key)
56
+ end
57
+ payload = normalized_extra_claims.merge(identity_fields)
58
+
59
+ issue(payload, signing_key: signing_key, algorithm: algorithm, ttl: ttl, issuer: issuer)
60
+ end
61
+
39
62
  def self.verify(token, verification_key:, **opts)
40
63
  algorithm = opts.fetch(:algorithm, 'HS256')
41
64
  verify_expiration = opts.fetch(:verify_expiration, true)
@@ -72,7 +72,8 @@ module Legion
72
72
  service_principal: nil,
73
73
  auth_path: 'auth/kerberos/login'
74
74
  },
75
- clusters: {}
75
+ clusters: {},
76
+ bootstrap_lease_ttl: 300
76
77
  }
77
78
  end
78
79
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Crypt
5
- VERSION = '1.5.2'
5
+ VERSION = '1.5.4'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-crypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -9,6 +9,20 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: concurrent-ruby
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: ed25519
14
28
  requirement: !ruby/object:Gem::Requirement