/*
* JBoss, Home of Professional Open Source.
* Copyright 2017, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.wildfly.test.security.common.elytron;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.jboss.as.test.shared.CliUtils.escapePath;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import org.jboss.as.test.integration.management.util.CLIWrapper;
/**
* Elytron credential store (keystore-based) configuration implementation.
*
* @author Josef Cacek
*/
public class SimpleCredentialStore extends AbstractConfigurableElement implements CredentialStore {
// Let's use the Path type for keystore location even the CLI fragment functionality is not used here.
private final Path keyStorePath;
private final CredentialReference credential;
private final String keyStoreType;
private final Boolean create;
private final Boolean modifiable;
private final Map<String, String> aliases;
private SimpleCredentialStore(Builder builder) {
super(builder);
this.keyStorePath = Objects.requireNonNull(builder.keyStorePath, "KeyStore path has to be provided");
this.credential = builder.credential;
this.keyStoreType = builder.keyStoreType;
this.create = builder.create;
this.modifiable = builder.modifiable;
this.aliases = Collections.unmodifiableMap(new HashMap<>(builder.aliases));
}
@Override
public void create(CLIWrapper cli) throws Exception {
// /subsystem=elytron/credential-store=test:add(location=a,create=true,modifiable=true,implementation-properties={"keyStoreType"=>"JCEKS"},
// credential-reference={clear-text=pass123})
final StringBuilder sb = new StringBuilder("/subsystem=elytron/credential-store=");
sb.append(name).append(":add(").append("location=")
.append(escapePath(keyStorePath.getPath()));
if (create != null) {
sb.append(",").append("create=").append(create.toString());
}
if (modifiable != null) {
sb.append(",").append("modifiable=").append(modifiable.toString());
}
if (keyStoreType != null) {
sb.append(",")
.append("implementation-properties={")
.append("\"keyStoreType\"=>\"")
.append(keyStoreType)
.append("\"}");
}
if (credential != null) {
sb.append(",").append(credential.asString());
}
if (isNotBlank(keyStorePath.getRelativeTo())) {
sb.append(",").append("relative-to=\"").append(keyStorePath.getRelativeTo()).append("\"");
}
sb.append(")");
cli.sendLine(sb.toString());
for (Entry<String, String> entry : aliases.entrySet()) {
// /subsystem=elytron/credential-store=test/alias=alias1:add(secret-value=mySecretValue)
cli.sendLine(String.format("/subsystem=elytron/credential-store=%s:add-alias(alias=%s, secret-value=\"%s\")", name,
entry.getKey(), entry.getValue()));
}
}
/**
* @see org.wildfly.test.security.common.elytron.ConfigurableElement#remove(org.jboss.as.test.integration.management.util.CLIWrapper)
*/
@Override
public void remove(CLIWrapper cli) throws Exception {
// remove aliases
for (String alias : aliases.keySet()) {
// lowercase alias used - https://issues.jboss.org/browse/WFLY-8131
cli.sendLine(String.format("/subsystem=elytron/credential-store=%s:remove-alias(alias=%s)", name, alias.toLowerCase(Locale.ROOT)));
}
cli.sendLine(String.format("/subsystem=elytron/credential-store=%s:remove()", name));
}
/**
* Creates builder to build {@link SimpleCredentialStore}.
*
* @return created builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder to build {@link SimpleCredentialStore}.
*/
public static final class Builder extends AbstractConfigurableElement.Builder<Builder> {
private Path keyStorePath;
private CredentialReference credential;
private String keyStoreType;
private Boolean create;
private Boolean modifiable;
private Map<String, String> aliases = new HashMap<>();
private Builder() {
}
public Builder withKeyStorePath(Path keyStorePath) {
this.keyStorePath = keyStorePath;
return this;
}
public Builder withCredential(CredentialReference credential) {
this.credential = credential;
return this;
}
public Builder withKeyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
return this;
}
public Builder withCreate(Boolean create) {
this.create = create;
return this;
}
public Builder withModifiable(Boolean modifiable) {
this.modifiable = modifiable;
return this;
}
/**
* Adds a named secret (alias + secret value) to the map of aliases to be created in the credential store.
*
* @param alias alias for the secret
* @param secret secret value
* @return
*/
public Builder withAlias(String alias, String secret) {
this.aliases.put(alias, secret);
return this;
}
/**
* Clears secrets map (aliases).
*
* @see #withAlias(String, String)
*/
public Builder clearAliases() {
this.aliases.clear();
return this;
}
public SimpleCredentialStore build() {
return new SimpleCredentialStore(this);
}
@Override
protected Builder self() {
return this;
}
}
}