Skip to content

Commit

Permalink
Commands for adding truststore authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
Radim Hatlapatka committed Jun 14, 2016
1 parent 4fad802 commit 7e0939b
Show file tree
Hide file tree
Showing 4 changed files with 401 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.wildfly.extras.creaper.commands.security.realms;

import org.wildfly.extras.creaper.commands.foundation.offline.xml.GroovyXmlTransform;
import org.wildfly.extras.creaper.commands.foundation.offline.xml.Subtree;
import org.wildfly.extras.creaper.core.offline.OfflineCommandContext;
import org.wildfly.extras.creaper.core.online.OnlineCommandContext;
import org.wildfly.extras.creaper.core.online.operations.Address;
import org.wildfly.extras.creaper.core.online.operations.Operations;
import org.wildfly.extras.creaper.core.online.operations.Values;
import org.wildfly.extras.creaper.core.online.operations.admin.Administration;

public class AddTruststoreAuthentication extends AbstractAddSecurityRealmSubElement {
private final String truststorePassword;
private String truststorePath;
private String truststoreProvider;
private String truststoreRelativeTo;

public AddTruststoreAuthentication(Builder builder) {
super(builder);
this.truststorePassword = builder.truststorePassword;
this.truststorePath = builder.truststorePath;
this.truststoreProvider = builder.truststoreProvider;
this.truststoreRelativeTo = builder.truststoreRelativeTo;
}

@Override
public final void apply(OfflineCommandContext ctx) throws Exception {
ctx.client.apply(GroovyXmlTransform.of(AddTruststoreAuthentication.class)
.subtree("management", Subtree.management())
.parameter("realmName", securityRealmName)
.parameter("truststorePassword", truststorePassword)
.parameter("truststorePath", truststorePath)
.parameter("truststoreProvider", truststoreProvider)
.parameter("truststoreRelativeTo", truststoreRelativeTo)
.parameter("replaceExisting", replaceExisting)
.build());
}

@Override
public final void apply(OnlineCommandContext ctx) throws Exception {
Address truststoreAuthAddress = securityRealmAddress.and("authentication", "truststore");

Operations ops = new Operations(ctx.client);

if (replaceExisting) {
boolean truststoreAuthExist = ops.exists(truststoreAuthAddress);
if (truststoreAuthExist) {
ops.remove(truststoreAuthAddress);
}
new Administration(ctx.client).reloadIfRequired();
}

ops.add(truststoreAuthAddress, Values.empty()
.and("keystore-password", truststorePassword)
.andOptional("keystore-path", truststorePath)
.andOptional("keystore-provider", truststoreProvider)
.andOptional("keystore-relative-to", truststoreRelativeTo));
}


public static final class Builder extends AbstractAddSecurityRealmSubElement.Builder<Builder> {

private String truststorePassword;
private String truststorePath;
private String truststoreProvider;
private String truststoreRelativeTo;

public Builder(String securityRealmName) {
super(securityRealmName);
}

/**
* Defines the password to open the truststore. It is mandatory parameter when also defining truststore
*/
public Builder truststorePassword(String truststorePassword) {
this.truststorePassword = truststorePassword;
return this;
}

/**
* Defines the path of the trustore, will be ignored if the truststore provider is anything other than JKS.
* If not defined, truststore is not defined for the server.
*/
public Builder truststorePath(String truststorePath) {
this.truststorePath = truststorePath;
return this;
}

/**
* Defines the provider for loading the truststore, defaults to JKS.
*/
public Builder truststoreProvider(String truststoreProvider) {
this.truststoreProvider = truststoreProvider;
return this;
}

/**
* Define name of another previously named path, or of one of the standard paths provided by the system.
* If 'relative-to' is provided, the value of the 'path' attribute is treated as relative to the path
* specified by this attribute.
*/
public Builder truststoreRelativeTo(String truststoreRelativeTo) {
this.truststoreRelativeTo = truststoreRelativeTo;
return this;
}

@Override
public AddTruststoreAuthentication build() {
if (truststorePath != null && truststorePassword == null) {
throw new IllegalArgumentException("truststorePassword is manadatory when defining also truststore");
}
return new AddTruststoreAuthentication(this);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
def securityRealm = management.'security-realms'.'security-realm'
def isSecurityRealmExist = securityRealm.any { it.'@name' == realmName }
if (!isSecurityRealmExist) {
throw new IllegalStateException("Security realm with name $realmName does not exist.")
}

def truststoreAttrs = [:]
if (truststorePassword != null) truststoreAttrs['keystore-password'] = truststorePassword
if (truststorePath != null) truststoreAttrs['path'] = truststorePath
if (truststoreProvider != null) truststoreAttrs['provider'] = truststoreProvider
if (truststoreRelativeTo != null) truststoreAttrs['relative-to'] = truststoreRelativeTo

def truststoreDefinition = {
'truststore'(truststoreAttrs)
}

def usedSecurityRealm = securityRealm.find { it.'@name' == realmName }
def authenticationElement = usedSecurityRealm.authentication
def isExistingAuthentication = authenticationElement.any { it.name() == 'authentication' }

if (isExistingAuthentication) {
def isTruststoreExists = authenticationElement.truststore.any { it.name() == 'truststore' }
if (isTruststoreExists && !replaceExisting) {
throw new IllegalStateException("Truststore authnetication already exists in security realm with name $realmName.")
} else {
if (isTruststoreExists) {
authenticationElement.truststore.find().replaceNode truststoreDefinition
} else {
authenticationElement.appendNode truststoreDefinition
}
}
} else {
usedSecurityRealm.appendNode {
'authentication'(truststoreDefinition)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package org.wildfly.extras.creaper.commands.security.realms;

import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.wildfly.extras.creaper.core.CommandFailedException;
import org.wildfly.extras.creaper.core.ManagementClient;
import org.wildfly.extras.creaper.core.offline.OfflineManagementClient;
import org.wildfly.extras.creaper.core.offline.OfflineOptions;

import java.io.File;

import static org.junit.Assert.fail;
import static org.wildfly.extras.creaper.XmlAssert.assertXmlIdentical;

public class AddTruststoreAuthenticationOfflineTest {

private static final String REALM_EMPTY = ""
+ "<server xmlns=\"urn:jboss:domain:1.7\">\n"
+ " <management>\n"
+ " <security-realms>\n"
+ " <security-realm name=\"realmName\"/>\n"
+ " </security-realms>\n"
+ " </management>\n"
+ "</server>";


private static final String REALM_WITH_TRUSTSTORE = ""
+ "<server xmlns=\"urn:jboss:domain:1.7\">\n"
+ " <management>\n"
+ " <security-realms>\n"
+ " <security-realm name=\"realmName\">\n"
+ " <authentication>\n"
+ " <truststore path=\"server.truststore\" relative-to=\"jboss.server.config.dir\" "
+ " keystore-password=\"password\" provider=\"JKS\"/>\n"
+ " </authentication>\n"
+ " </security-realm>\n"
+ " </security-realms>\n"
+ " </management>\n"
+ "</server>";


@Rule
public final TemporaryFolder tmp = new TemporaryFolder();

@Before
public void setUp() {
XMLUnit.setNormalizeWhitespace(true);
}

@Test
public void addSimple() throws Exception {
File cfg = tmp.newFile("xmlTransform.xml");
Files.write(REALM_EMPTY, cfg, Charsets.UTF_8);

OfflineManagementClient client = ManagementClient.offline(
OfflineOptions.standalone().configurationFile(cfg).build());

AddTruststoreAuthentication cmd = new AddTruststoreAuthentication.Builder("realmName")
.truststorePassword("password")
.truststorePath("server.truststore")
.truststoreRelativeTo("jboss.server.config.dir")
.truststoreProvider("JKS")
.build();

assertXmlIdentical(REALM_EMPTY, Files.toString(cfg, Charsets.UTF_8));
client.apply(cmd);
assertXmlIdentical(REALM_WITH_TRUSTSTORE, Files.toString(cfg, Charsets.UTF_8));
}

@Test(expected = CommandFailedException.class)
public void addExisting() throws Exception {
File cfg = tmp.newFile("xmlTransform.xml");
Files.write(REALM_EMPTY, cfg, Charsets.UTF_8);

OfflineManagementClient client = ManagementClient.offline(
OfflineOptions.standalone().configurationFile(cfg).build());

AddTruststoreAuthentication cmd1 = new AddTruststoreAuthentication.Builder("realmName")
.truststorePassword("password")
.truststorePath("server.truststore")
.truststoreRelativeTo("jboss.server.config.dir")
.truststoreProvider("JKS")
.build();

AddTruststoreAuthentication cmd2 = new AddTruststoreAuthentication.Builder("realmName")
.truststorePassword("password")
.truststorePath("server2.truststore")
.truststoreRelativeTo("jboss.server.config.dir")
.truststoreProvider("JKS")
.build();

client.apply(cmd1);
client.apply(cmd2);
fail("Truststore authentication already exists in configuration, exception should be thrown");
}

@Test
public void replaceExisting() throws Exception {
File cfg = tmp.newFile("xmlTransform.xml");
Files.write(REALM_EMPTY, cfg, Charsets.UTF_8);

OfflineManagementClient client = ManagementClient.offline(
OfflineOptions.standalone().configurationFile(cfg).build());


AddTruststoreAuthentication cmd1 = new AddTruststoreAuthentication.Builder("realmName")
.truststorePassword("password")
.truststorePath("server2.truststore")
.truststoreRelativeTo("jboss.server.config.dir")
.truststoreProvider("JKS")
.build();

AddTruststoreAuthentication cmd2 = new AddTruststoreAuthentication.Builder("realmName")
.truststorePassword("password")
.truststorePath("server.truststore")
.truststoreRelativeTo("jboss.server.config.dir")
.truststoreProvider("JKS")
.replaceExisting()
.build();

assertXmlIdentical(REALM_EMPTY, Files.toString(cfg, Charsets.UTF_8));
client.apply(cmd1);
client.apply(cmd2);
assertXmlIdentical(REALM_WITH_TRUSTSTORE, Files.toString(cfg, Charsets.UTF_8));
}
}
Loading

0 comments on commit 7e0939b

Please sign in to comment.