-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added command for adding secret authentication for security realm.
- Loading branch information
Showing
4 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
83 changes: 83 additions & 0 deletions
83
...ain/java/org/wildfly/extras/creaper/commands/security/realms/AddSecretAuthentication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...ources/org/wildfly/extras/creaper/commands/security/realms/AddSecretAuthentication.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
115 changes: 115 additions & 0 deletions
115
...g/wildfly/extras/creaper/commands/security/realms/AddSecretAuthenticationOfflineTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
...rg/wildfly/extras/creaper/commands/security/realms/AddSecretAuthenticationOnlineTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} |