Class: HasHelpers::ServiceTrust::KeyManager

Inherits:
Object
  • Object
show all
Defined in:
lib/has_helpers/service_trust/key_manager.rb

Defined Under Namespace

Classes: EnvKey

Constant Summary collapse

ADVISORY_LOCK_KEY =
8_472_901

Class Method Summary collapse

Class Method Details

.current_signing_keyObject



30
31
32
33
34
35
36
# File 'lib/has_helpers/service_trust/key_manager.rb', line 30

def current_signing_key
  cache.synchronize do
    @current_signing_key ||= load_from_env || load_or_generate_from_db
    raise_if_env_mismatch!(@current_signing_key)
    @current_signing_key
  end
end

.envObject



82
83
84
# File 'lib/has_helpers/service_trust/key_manager.rb', line 82

def env
  HasHelpers::ServiceTrust.config.environment.to_s
end

.generate_first_key!Object



69
70
71
# File 'lib/has_helpers/service_trust/key_manager.rb', line 69

def generate_first_key!
  load_or_generate_from_db
end

.generate_keypairObject



86
87
88
# File 'lib/has_helpers/service_trust/key_manager.rb', line 86

def generate_keypair
  OpenSSL::PKey::RSA.generate(2048)
end

.invalidate_cache!Object



78
79
80
# File 'lib/has_helpers/service_trust/key_manager.rb', line 78

def invalidate_cache!
  cache.synchronize { @current_signing_key = nil }
end

.retire!(kid) ⇒ Object



73
74
75
76
# File 'lib/has_helpers/service_trust/key_manager.rb', line 73

def retire!(kid)
  HasHelpers::ServiceTrust::Key.for_env(env).find_by!(kid: kid).update!(active: false, retired_at: Time.current, verifiable_until: Time.current)
  invalidate_cache!
end

.rotate!(verifiable_for: 1.hour) ⇒ Object

Raises:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/has_helpers/service_trust/key_manager.rb', line 44

def rotate!(verifiable_for: 1.hour)
  raise ConfigurationError, "cannot rotate ENV-backed ServiceTrust keys" if load_from_env

  HasHelpers::ServiceTrust::Key.transaction do
    with_advisory_lock do
      old = HasHelpers::ServiceTrust::Key.active_for_env(env).first
      keypair = generate_keypair
      # retired_at is set to a FUTURE timestamp so the key stays in JWKS
      # (verifiable_for_env scope keeps rows where retired_at IS NULL OR retired_at > NOW)
      # until the rotation overlap window elapses.
      old&.update!(active: false, verifiable_until: Time.current + verifiable_for, retired_at: Time.current + verifiable_for)
      new_key = HasHelpers::ServiceTrust::Key.create!(
        environment: env,
        kid: new_kid,
        public_key_pem: keypair.public_key.to_pem,
        private_key_pem: keypair.to_pem,
        algorithm: "RS256",
        active: true
      )
      invalidate_cache!
      new_key
    end
  end
end

.verifiable_keysObject



38
39
40
41
42
# File 'lib/has_helpers/service_trust/key_manager.rb', line 38

def verifiable_keys
  current = current_signing_key
  db_keys = defined?(HasHelpers::ServiceTrust::Key) ? HasHelpers::ServiceTrust::Key.verifiable_for_env(env).to_a : []
  ([current] + db_keys.reject { |key| same_key?(key, current) }).uniq(&:kid)
end