Skip to content

Commit

Permalink
Added command for adding secret authentication for security realm.
Browse files Browse the repository at this point in the history
  • Loading branch information
istraka committed Apr 7, 2016
1 parent 72167cb commit baf09e2
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.wildfly.extras.creaper.commands.security.realms;

import com.google.common.io.BaseEncoding;
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;

import java.io.IOException;

public final class AddSecretAuthentication extends AbstractAddSecurityRealmSubElement {
private final String password;

protected AddSecretAuthentication(Builder builder) {
super(builder);
password = builder.password;
}

@Override
public void apply(OfflineCommandContext ctx) throws Exception {
ctx.client.apply(GroovyXmlTransform.of(AddSecretAuthentication.class)
.subtree("management", Subtree.management())
.parameter("realmName", securityRealmName)
.parameter("password", password)
.parameter("replaceExisting", replaceExisting)
.build());
}

@Override
public void apply(OnlineCommandContext ctx) throws Exception {
Address secretServerIdentitiesAddress = securityRealmAddress.and("server-identity", "secret");

Operations ops = new Operations(ctx.client);

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

ops.add(secretServerIdentitiesAddress, Values.empty()
.and("value", password));
}

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

private String password;

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

/**
* This method will encode the provided {@code password} using Base64
*/
public Builder password(String password) throws IOException {
this.password = BaseEncoding.base64().encode(password.getBytes("UTF-8"));
return this;
}

/**
* This method expects the provided {@code password} to be already encoded using Base64.
*/
public Builder passwordBase64(String password) throws IOException {
this.password = password;
return this;
}

@Override
public AddSecretAuthentication build() {
if (password == null) {
throw new IllegalArgumentException("Password must be specified");
}
return new AddSecretAuthentication(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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 serverIdentityDefinition = {
secret(value: password)
}

def usedSecurityRealm = securityRealm.find { it.'@name' == realmName }

def serverIdentities = usedSecurityRealm.'server-identities'
def isExistingServerIdentities = serverIdentities.any { it.name() == 'server-identities' }
if (isExistingServerIdentities) {
def isExistingSecret = serverIdentities.secret.any { it.name() == 'secret' }
if (isExistingSecret && !replaceExisting) {
throw new IllegalStateException("Secret server identity already exists in security realm with name $realmName.")
} else {
if (isExistingSecret) {
serverIdentities.secret.find().replaceNode serverIdentityDefinition
} else {
serverIdentities.appendNode serverIdentityDefinition
}
}
} else {
usedSecurityRealm.appendNode {
'server-identities'(serverIdentityDefinition)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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 AddSecretAuthenticationOfflineTest {

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_SECRET = ""
+ "<server xmlns=\"urn:jboss:domain:1.7\">\n"
+ " <management>\n"
+ " <security-realms>\n"
+ " <security-realm name=\"realmName\">\n"
+ " <server-identities>\n"
+ " <secret value=\"cGFzc3dvcmQx\"/>\n"
+ " </server-identities>\n"
+ " </security-realm>\n"
+ " </security-realms>\n"
+ " </management>\n"
+ "</server>";

private static final String PASSWORD = "password1";

@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());

AddSecretAuthentication cmd = new AddSecretAuthentication.Builder("realmName")
.password("password1")
.build();

assertXmlIdentical(REALM_EMPTY, Files.toString(cfg, Charsets.UTF_8));
client.apply(cmd);
assertXmlIdentical(REALM_WITH_SECRET, 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());

AddSecretAuthentication cmd1 = new AddSecretAuthentication.Builder("realmName")
.password("password1")
.build();

AddSecretAuthentication cmd2 = new AddSecretAuthentication.Builder("realmName")
.password("password2")
.build();

client.apply(cmd1);
client.apply(cmd2);
fail("Secret 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());


AddSecretAuthentication cmd1 = new AddSecretAuthentication.Builder("realmName")
.password("password2")
.build();

AddSecretAuthentication cmd2 = new AddSecretAuthentication.Builder("realmName")
.password("password1")
.replaceExisting()
.build();

assertXmlIdentical(REALM_EMPTY, Files.toString(cfg, Charsets.UTF_8));
client.apply(cmd1);
client.apply(cmd2);
assertXmlIdentical(REALM_WITH_SECRET, Files.toString(cfg, Charsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package org.wildfly.extras.creaper.commands.security.realms;

import com.google.common.io.BaseEncoding;
import org.jboss.arquillian.junit.Arquillian;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.extras.creaper.core.CommandFailedException;
import org.wildfly.extras.creaper.core.ManagementClient;
import org.wildfly.extras.creaper.core.online.CliException;
import org.wildfly.extras.creaper.core.online.ModelNodeResult;
import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
import org.wildfly.extras.creaper.core.online.OnlineOptions;
import org.wildfly.extras.creaper.core.online.operations.Address;
import org.wildfly.extras.creaper.core.online.operations.OperationException;
import org.wildfly.extras.creaper.core.online.operations.Operations;
import org.wildfly.extras.creaper.core.online.operations.admin.Administration;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

@RunWith(Arquillian.class)
public class AddSecretAuthenticationOnlineTest {

private static final String TEST_REALM_NAME = "creaperSecretRealm";
private static final Address TEST_REALM_ADDRESS
= Address.coreService("management").and("security-realm", TEST_REALM_NAME);
private static final Address TEST_SECRET_IDENTITY_ADDRESS
= TEST_REALM_ADDRESS.and("server-identity", "secret");
private static final String PASSWORD = "password1";

private OnlineManagementClient client;
private Operations ops;
private Administration administration;

@Before
public void connect() throws Exception {
client = ManagementClient.online(OnlineOptions.standalone().localDefault().build());
ops = new Operations(client);
administration = new Administration(client);

AddSecurityRealm addSecurityRealm = new AddSecurityRealm.Builder(TEST_REALM_NAME).build();
client.apply(addSecurityRealm);
assertTrue("The security realm should be created", ops.exists(TEST_REALM_ADDRESS));
}

@Test
public void add() throws Exception {
client.apply(new AddSecretAuthentication.Builder(TEST_REALM_NAME)
.password(PASSWORD)
.build());
assertTrue("Server identity should be created", ops.exists(TEST_SECRET_IDENTITY_ADDRESS));
ModelNodeResult result = ops.readAttribute(TEST_SECRET_IDENTITY_ADDRESS, "value");
result.assertSuccess();
assertEquals("Password should be encoded properly.",
PASSWORD, new String(BaseEncoding.base64().decode(result.stringValue())));
}

@Test(expected = CommandFailedException.class)
public void addExisting() throws Exception {
client.apply(new AddSecretAuthentication.Builder(TEST_REALM_NAME)
.password(PASSWORD)
.build());
assertTrue("Server identyty should be created", ops.exists(TEST_SECRET_IDENTITY_ADDRESS));
client.apply(new AddSecretAuthentication.Builder(TEST_REALM_NAME)
.password(PASSWORD)
.build());
fail("Secret authentication method has been already configured. Should have failed.");
}

@Test
public void replaceExisting() throws Exception {
client.apply(new AddSecretAuthentication.Builder(TEST_REALM_NAME)
.password("secret")
.build());
assertTrue("Server identyty should be created", ops.exists(TEST_SECRET_IDENTITY_ADDRESS));

client.apply(new AddSecretAuthentication.Builder(TEST_REALM_NAME)
.password(PASSWORD)
.replaceExisting()
.build());
assertTrue("Server identity should be created", ops.exists(TEST_SECRET_IDENTITY_ADDRESS));
ModelNodeResult result = ops.readAttribute(TEST_SECRET_IDENTITY_ADDRESS, "value");
result.assertSuccess();
assertEquals("Password should be encoded properly.",
PASSWORD, new String(BaseEncoding.base64().decode(result.stringValue())));
}

@After
public void cleanup() throws IOException, CliException, OperationException, TimeoutException, InterruptedException {
try {
ops.removeIfExists(TEST_REALM_ADDRESS);
administration.reloadIfRequired();
} finally {
client.close();
}
}
}

0 comments on commit baf09e2

Please sign in to comment.