001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.secret; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022import java.util.concurrent.*; 023 024/** 025 * Built-in default {@link SecretStore}: a per-process, {@link ConcurrentHashMap}-backed key-to-secret map. 026 * 027 * <p> 028 * This is the zero-config store {@link SecretStores#resolve(org.apache.juneau.commons.inject.BeanStore)} falls back 029 * to when no store has been contributed. It is honest about its limits: 030 * 031 * <p> 032 * <b>Process-local only.</b> Secrets live in this JVM's heap and are gone when the process exits — this store is 033 * <b>not</b> persistent. Two separate JVMs (or two instances behind a load balancer) each hold their own map and do 034 * <b>not</b> see each other's secrets, so it is <b>not</b> cross-node. It is a convenience default and a test double, 035 * not a production secret backend; deployments that need durability or sharing must contribute a store backed by an 036 * external secret manager. 037 * 038 * <p> 039 * <b>Defensive copies.</b> {@link #store} copies the supplied array and {@link #find} returns a fresh copy, so the 040 * caller may zero its own arrays without disturbing the stored value and vice-versa. {@link #delete} zeroes the 041 * retained array before dropping it. The value is never {@code toString()}'d, logged, or dumped. 042 * 043 * <p> 044 * <b>Thread-safety.</b> All operations are safe for concurrent invocation. 045 * 046 * @since 10.0.0 047 */ 048public class InMemorySecretStore implements SecretStore { 049 050 private final ConcurrentHashMap<String,char[]> secrets = new ConcurrentHashMap<>(); 051 052 @Override /* SecretStore */ 053 public void store(String key, char[] secret) { 054 reqnn("key", key); 055 reqnn("secret", secret); 056 secrets.put(key, secret.clone()); 057 } 058 059 @Override /* SecretStore */ 060 public Optional<char[]> find(String key) { 061 reqnn("key", key); 062 var v = secrets.get(key); 063 return v == null ? oe() : o(v.clone()); 064 } 065 066 @Override /* SecretStore */ 067 public boolean exists(String key) { 068 reqnn("key", key); 069 return secrets.containsKey(key); 070 } 071 072 @Override /* SecretStore */ 073 public boolean delete(String key) { 074 reqnn("key", key); 075 var v = secrets.remove(key); 076 if (v == null) 077 return false; 078 Arrays.fill(v, '\0'); 079 return true; 080 } 081}