KEYCLOAK-10933 Interfaces for vault SPI

This commit is contained in:
Hynek Mlnarik 2019-07-30 19:02:19 +02:00 committed by Hynek Mlnařík
parent 71eed3af06
commit d2da206d6b
6 changed files with 232 additions and 1 deletions

View file

@ -0,0 +1,50 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.vault;
import org.keycloak.provider.Provider;
/**
* Provider interface for a vault. The only purpose of a vault is retrieval of secrets.
*/
public interface VaultProvider extends Provider {
/**
* Retrieves a secret from vault. The implementation should respect
* at least the realm ID to separate the secrets within the vault.
* If the secret is retrieved successfully, it is returned;
* otherwise this method results into an empty {@link VaultRawSecret#getRawSecret()}.
*
* This method is intended to be used within a try-with-resources block so that
* the secret is destroyed immediately after use.
*
* Note that it is responsibility of the implementor to provide a way
* to destroy the secret in the returned {@link VaultRawSecret#close()} method.
*
* @param vaultSecretId Identifier of the secret. It corresponds to the value
* entered by user in the respective configuration, which in turn
* is obtained from the vault when storing the secret.
*
* @return Always a non-{@code null} value with the raw secret.
* Within the returned value, the secret or {@code null} is stored in the
* {@link VaultRawSecret#getRawSecret()} return value if the secret was successfully
* resolved, or an empty {@link java.util.Optional} if the secret has not been found in the vault.
*/
VaultRawSecret obtainSecret(String vaultSecretId);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.vault;
import org.keycloak.provider.ProviderFactory;
/**
*/
public interface VaultProviderFactory extends ProviderFactory<VaultProvider> {
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.vault;
import java.nio.ByteBuffer;
import java.util.Optional;
/**
* Raw representation of the secret obtained from vault that supports automated cleanup of memory.
*
* @author hmlnarik
*/
public interface VaultRawSecret extends AutoCloseable {
/**
* Returns the raw secret bytes.
* @return If the secret was successfully resolved by vault, returns
* an {@link Optional} containing the value returned by the vault
* (a valid value can be {@code null}), or an empty {@link Optional}
*/
Optional<ByteBuffer> getRawSecret();
/**
* Destroys the secret in memory by e.g. overwriting it with random garbage.
*/
@Override
void close();
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.vault;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* SPI for a low-level vault access.
*/
public class VaultSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "vault";
}
@Override
public Class<? extends Provider> getProviderClass() {
return VaultProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return VaultProviderFactory.class;
}
}

View file

@ -74,4 +74,5 @@ org.keycloak.keys.KeySpi
org.keycloak.storage.client.ClientStorageProviderSpi org.keycloak.storage.client.ClientStorageProviderSpi
org.keycloak.crypto.SignatureSpi org.keycloak.crypto.SignatureSpi
org.keycloak.crypto.ClientSignatureVerifierSpi org.keycloak.crypto.ClientSignatureVerifierSpi
org.keycloak.crypto.HashSpi org.keycloak.crypto.HashSpi
org.keycloak.vault.VaultSpi

View file

@ -0,0 +1,64 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.vault;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
/**
* Default raw secret implementation for {@code byte[]}.
* @author hmlnarik
*/
public class DefaultVaultRawSecret implements VaultRawSecret {
private static final VaultRawSecret EMPTY_VAULT_SECRET = new VaultRawSecret() {
@Override
public Optional<ByteBuffer> getRawSecret() {
return Optional.empty();
}
@Override
public void close() {
}
};
private final ByteBuffer rawSecret;
public static VaultRawSecret forBuffer(Optional<ByteBuffer> buffer) {
if (buffer == null || ! buffer.isPresent()) {
return EMPTY_VAULT_SECRET;
}
return new DefaultVaultRawSecret(buffer.get());
}
private DefaultVaultRawSecret(ByteBuffer rawSecret) {
this.rawSecret = rawSecret;
}
@Override
public Optional<ByteBuffer> getRawSecret() {
return Optional.of(this.rawSecret);
}
@Override
public void close() {
if (rawSecret.hasArray()) {
ThreadLocalRandom.current().nextBytes(rawSecret.array());
}
}
}