001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.common.secret.context; 019 020import dev.enola.common.context.TLC; 021import dev.enola.common.secret.Secret; 022import dev.enola.common.secret.SecretManager; 023import dev.enola.common.secret.UnavailableSecretManager; 024 025import java.io.IOException; 026import java.util.Optional; 027 028/** 029 * SecretManagerTLC is a {@link SecretManager} implementation that looks up the current {@link 030 * SecretManager} from the {@link TLC}. If it's not found there, then it falls back to one passed to 031 * the constructor, which defaults to an {@link dev.enola.common.secret.UnavailableSecretManager}. 032 */ 033public class SecretManagerTLC implements SecretManager { 034 035 // We probably don't really need a Singleton<SecretManager> ... 036 // public static final Singleton<SecretManager> SINGLETON = new Singleton<>() {}; 037 038 private final SecretManager fallback; 039 040 /** Creates a new instance which falls back to the SecretManager passed to this constructor. */ 041 public SecretManagerTLC(SecretManager fallback) { 042 this.fallback = fallback; 043 } 044 045 public SecretManagerTLC() { 046 this(new UnavailableSecretManager()); 047 } 048 049 @Override 050 public void store(String key, char[] value) throws IOException { 051 delegate().store(key, value); 052 } 053 054 @Override 055 public Optional<Secret> getOptional(String key) throws IOException { 056 return delegate().getOptional(key); 057 } 058 059 @Override 060 public Secret get(String key) throws IllegalStateException, IOException { 061 return delegate().get(key); 062 } 063 064 @Override 065 public void delete(String key) throws IOException { 066 delegate().delete(key); 067 } 068 069 private SecretManager delegate() { 070 return TLC.optional(SecretManager.class).orElse(fallback); 071 } 072}