Skip to content

Commit

Permalink
WFLY-8077 datasource subsystem - credential-reference doesn't work #2
Browse files Browse the repository at this point in the history
…fix credential store service dependency
  • Loading branch information
maeste committed Feb 16, 2017
1 parent f4517a6 commit 17ce9af
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 137 deletions.
Expand Up @@ -145,7 +145,7 @@ public void getResourceValue(final ResolutionContext context, final ServiceBuild

clearUnknownProperties(reflectionIndex, clazz, properties);
populateProperties(reflectionIndex, clazz, properties);
DsSecurityImpl dsSecurity = new DsSecurityImpl(user, password, null, false, null);
DsSecurityImpl dsSecurity = new DsSecurityImpl(user, password, null, false, null, null);

if (XADataSource.class.isAssignableFrom(clazz) && transactional) {
final DsXaPoolImpl xaPool = new DsXaPoolImpl(minPoolSize < 0 ? Defaults.MIN_POOL_SIZE : Integer.valueOf(minPoolSize),
Expand Down
Expand Up @@ -87,7 +87,7 @@ protected DsSecurity parseDsSecurity(XMLStreamReader reader) throws XMLStreamExc
DataSource.Tag.SECURITY) {

return new DsSecurityImpl(userName, password,elytronEnabled? authenticationContext: securityDomain,
elytronEnabled, reauthPlugin);
elytronEnabled, reauthPlugin, null);
} else {
if (DsSecurity.Tag.forName(reader.getLocalName()) == DsSecurity.Tag.UNKNOWN) {
throw new ParserException(bundle.unexpectedEndTag(reader.getLocalName()));
Expand Down
Expand Up @@ -897,4 +897,8 @@ public interface ConnectorLogger extends BasicLogger {

@Message(id = 109, value = "Callback with security domain is required - use createCallbackHandler(Callback callback) instead")
UnsupportedOperationException unsupportedCreateCallbackHandlerMethod();

@Message(id = 110, value = "CredentialSourceSupplier is invalid for DSSecurity")
IllegalStateException invalidCredentialSourceSupplier(@Cause Throwable cause);

}
Expand Up @@ -15,26 +15,45 @@
*/
package org.jboss.as.connector.metadata.ds;

import java.util.Objects;

import org.jboss.as.connector.logging.ConnectorLogger;
import org.jboss.as.connector.metadata.api.common.Credential;
import org.jboss.as.connector.metadata.api.ds.DsSecurity;
import org.jboss.jca.common.CommonBundle;
import org.jboss.jca.common.api.metadata.common.Extension;
import org.jboss.jca.common.api.validator.ValidateException;
import org.jboss.logging.Messages;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.password.interfaces.ClearPassword;

/**
* Extension of {@link org.jboss.jca.common.metadata.ds.DsSecurityImpl} with added Elytron support.
*
* @author Flavia Rainone
*/
public class DsSecurityImpl
extends org.jboss.jca.common.metadata.ds.DsSecurityImpl implements DsSecurity, Credential {
public class DsSecurityImpl implements DsSecurity, Credential {

private static final long serialVersionUID = 312322268048179001L;

private static CommonBundle bundle = (CommonBundle) Messages.getBundle(CommonBundle.class);
private final String userName;
private final String password;
private final String securityDomain;
private final Extension reauthPlugin;

/**
* Indicates if the Credential data belongs to Elytron or PicketBox.
*/
private boolean elytronEnabled;

private final ExceptionSupplier<CredentialSource, Exception> credentialSourceSupplier;




/**
* Create a new DsSecurityImpl.
*
Expand All @@ -45,14 +64,50 @@ public class DsSecurityImpl
* @param elytronEnabled indicates if elytron is enabled. In this case, {@param securityContext}, defined as
* securityDomain in super class, refers to an Elytron authentication context
* @param reauthPlugin reauthentication plugin
* @param credentialSourceSupplier an Elytron credentia supplier
* @throws ValidateException in case of validation error
*/
public DsSecurityImpl(String userName, String password, String securityContext, boolean elytronEnabled,
Extension reauthPlugin) throws ValidateException {
super(userName, password, securityContext, reauthPlugin);
public DsSecurityImpl(final String userName, final String password, final String securityContext, final boolean elytronEnabled,
Extension reauthPlugin, final ExceptionSupplier<CredentialSource, Exception> credentialSourceSupplier) throws ValidateException {
this.userName = userName;
this.password = password;
this.securityDomain = securityContext;
this.reauthPlugin = reauthPlugin;
this.elytronEnabled = elytronEnabled;
this.credentialSourceSupplier = credentialSourceSupplier;
this.validate();
}

public final String getSecurityDomain() {
return this.securityDomain;
}

public final String resolveSecurityDomain() {
return this.getSecurityDomain();
}

public final String getUserName() {
return this.userName;
}

public final String getPassword() {
if (credentialSourceSupplier != null) {
try {
return new String(
credentialSourceSupplier.get().getCredential(PasswordCredential.class).getPassword(ClearPassword.class).getPassword());
} catch (Exception e) {
throw ConnectorLogger.DEPLOYMENT_CONNECTOR_LOGGER.invalidCredentialSourceSupplier(e);
}
}
return this.password;

}

public Extension getReauthPlugin() {
return this.reauthPlugin;
}


/**
* Indicates if Elytron is enabled. In this case, {@link #getSecurityDomain()}, refers to an Elytron authentication context
*
Expand All @@ -63,71 +118,41 @@ public final boolean isElytronEnabled() {
return elytronEnabled;
}

/**
* {@inheritDoc}
*/

public void validate() throws ValidateException {
if (this.userName != null && this.securityDomain != null) {
throw new ValidateException(bundle.invalidSecurityConfiguration());
}
}


@Override
public int hashCode() {
return super.hashCode() + (elytronEnabled? 1: 0);
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DsSecurityImpl that = (DsSecurityImpl) o;
return elytronEnabled == that.elytronEnabled &&
Objects.equals(userName, that.userName) &&
Objects.equals(password, that.password) &&
Objects.equals(securityDomain, that.securityDomain) &&
Objects.equals(reauthPlugin, that.reauthPlugin) &&
Objects.equals(credentialSourceSupplier, that.credentialSourceSupplier);
}

/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof DsSecurityImpl))
return false;
DsSecurityImpl other = (DsSecurityImpl) obj;
return elytronEnabled == other.elytronEnabled && super.equals(other);
public int hashCode() {
return Objects.hash(userName, password, securityDomain, reauthPlugin, elytronEnabled, credentialSourceSupplier);
}

/**
* {@inheritDoc}
*/
@Override
public String toString() {
if (!elytronEnabled)
return super.toString();

StringBuilder sb = new StringBuilder();

sb.append("<security>");
sb.append("<").append(DsSecurity.Tag.ELYTRON_ENABLED).append("/>");
if (getSecurityDomain() != null) {
sb.append("<").append(DsSecurity.Tag.AUTHENTICATION_CONTEXT).append(">");
sb.append(getSecurityDomain());
sb.append("</").append(DsSecurity.Tag.AUTHENTICATION_CONTEXT).append(">");
}

if (getReauthPlugin() != null) {
sb.append("<").append(DsSecurity.Tag.REAUTH_PLUGIN);
sb.append(" ").append(Extension.Attribute.CLASS_NAME).append("=\"");
sb.append(getReauthPlugin().getClassName()).append("\"");
sb.append(">");

if (getReauthPlugin().getConfigPropertiesMap().size() > 0) {
java.util.Iterator<java.util.Map.Entry<String, String>> it = getReauthPlugin().getConfigPropertiesMap().entrySet().iterator();

while (it.hasNext()) {
java.util.Map.Entry<String, String> entry = it.next();

sb.append("<").append(Extension.Tag.CONFIG_PROPERTY);
sb.append(" name=\"").append(entry.getKey()).append("\">");
sb.append(entry.getValue());
sb.append("</").append(Extension.Tag.CONFIG_PROPERTY).append(">");
}
}

sb.append("</").append(DsSecurity.Tag.REAUTH_PLUGIN).append(">");
}

sb.append("</security>");

return sb.toString();
return "DsSecurityImpl{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
", securityDomain='" + securityDomain + '\'' +
", reauthPlugin=" + reauthPlugin +
", elytronEnabled=" + elytronEnabled +
", credentialSourceSupplier=" + credentialSourceSupplier +
'}';
}
}
Expand Up @@ -53,12 +53,10 @@
import org.jboss.as.connector.util.ConnectorServices;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.security.CredentialReference;
Expand All @@ -83,7 +81,6 @@
import org.jboss.jca.core.spi.transaction.TransactionIntegration;
import org.jboss.jca.deployers.common.CommonDeployment;
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
Expand All @@ -92,12 +89,8 @@
import org.jboss.msc.service.ValueInjectionService;
import org.jboss.security.SubjectFactory;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.credential.source.CredentialStoreCredentialSource;
import org.wildfly.security.credential.store.CredentialStore;
import org.wildfly.security.password.interfaces.ClearPassword;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.security.credential.source.CredentialSource;

/**
* Abstract operation handler responsible for adding a DataSource.
Expand Down Expand Up @@ -448,37 +441,4 @@ static Collection<AttributeDefinition> join(final AttributeDefinition[] a, final
return result;
}

private static char[] getPasswordFromCredentialReference(ObjectTypeAttributeDefinition credentialReferenceDefinition, OperationContext context, ModelNode model) throws OperationFailedException {
ModelNode value = credentialReferenceDefinition.resolveModelAttribute(context, model);
if (value.isDefined()) {

final String credentialStoreName = CredentialReference.credentialReferencePartAsStringIfDefined(value, CredentialReference.STORE);
final String credentialAlias = CredentialReference.credentialReferencePartAsStringIfDefined(value, CredentialReference.ALIAS);
final String secret = CredentialReference.credentialReferencePartAsStringIfDefined(value, CredentialReference.CLEAR_TEXT);

if (secret != null) {
return secret.toCharArray();
}

//CredentialSource cs = CredentialReference.getCredentialSourceSupplier(context, credentialReferenceDefinition, model, serviceBuilder);
String credentialStoreCapabilityName = RuntimeCapability.buildDynamicCapabilityName(CredentialReference.CREDENTIAL_STORE_CAPABILITY, credentialStoreName);
ServiceName credentialStoreServiceName = context.getCapabilityServiceName(credentialStoreCapabilityName, CredentialStore.class);
Service<?> csService = context.getServiceRegistry(false).getService(credentialStoreServiceName).getService();
CredentialStore credentialStore = csService.getValue() instanceof CredentialStore ? (CredentialStore) csService.getValue() : null;

if (credentialAlias != null && credentialStore != null) {
CredentialSource cs = new CredentialStoreCredentialSource(credentialStore, credentialAlias);

try {
return cs.getCredential(PasswordCredential.class).getPassword(ClearPassword.class).getPassword();
} catch (Exception e) {
throw new OperationFailedException(e);
}
}
return null;
} else {
return null;
}
}

}
Expand Up @@ -129,9 +129,7 @@
import org.jboss.jca.common.metadata.ds.TimeOutImpl;
import org.jboss.jca.common.metadata.ds.ValidationImpl;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.password.interfaces.ClearPassword;

/**
* Utility used to help convert between JCA spi data-source instances and model
Expand Down Expand Up @@ -177,20 +175,7 @@ static ModifiableDataSource from(final OperationContext operationContext, final
final DsPool pool = new DsPoolImpl(minPoolSize, initialPoolSize, maxPoolSize, prefill, useStrictMin, flushStrategy, allowMultipleUsers, capacity, fair, connectionListener);

final String username = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, USERNAME);
final String password;
try {
CredentialSource cs = null;
if (credentialSourceSupplier != null)
cs = credentialSourceSupplier.get();
if (cs != null) {
password = new String(
cs.getCredential(PasswordCredential.class).getPassword(ClearPassword.class).getPassword());
} else {
password = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, PASSWORD);
}
} catch (Exception e) {
throw new OperationFailedException(e);
}
final String password = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, PASSWORD);


final String securityDomain = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, SECURITY_DOMAIN);
Expand All @@ -200,7 +185,7 @@ static ModifiableDataSource from(final OperationContext operationContext, final
final Extension reauthPlugin = ModelNodeUtil.extractExtension(operationContext, dataSourceNode, REAUTH_PLUGIN_CLASSNAME, REAUTHPLUGIN_PROPERTIES);

final DsSecurity security = new DsSecurityImpl(username, password,
elytronEnabled? authenticationContext: securityDomain, elytronEnabled, reauthPlugin);
elytronEnabled? authenticationContext: securityDomain, elytronEnabled, reauthPlugin, credentialSourceSupplier);

final boolean sharePreparedStatements = ModelNodeUtil.getBooleanIfSetOrGetDefault(operationContext, dataSourceNode, SHARE_PREPARED_STATEMENTS);
final Long preparedStatementsCacheSize = ModelNodeUtil.getLongIfSetOrGetDefault(operationContext, dataSourceNode, PREPARED_STATEMENTS_CACHE_SIZE);
Expand Down Expand Up @@ -292,25 +277,15 @@ static ModifiableXaDataSource xaFrom(final OperationContext operationContext, fi
isSameRmOverride, interleaving, padXid, wrapXaDataSource, noTxSeparatePool, allowMultipleUsers, capacity, fair, connectionListener);

final String username = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, USERNAME);
final String password;
if (credentialSourceSupplier != null) {
try {
password = new String(
credentialSourceSupplier.get().getCredential(PasswordCredential.class).getPassword(ClearPassword.class).getPassword());
} catch (Exception e) {
throw new OperationFailedException(e);
}
} else {
password = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, PASSWORD);
}
final String password= ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, PASSWORD);
final String securityDomain = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, SECURITY_DOMAIN);
final boolean elytronEnabled = ModelNodeUtil.getBooleanIfSetOrGetDefault(operationContext, dataSourceNode, ELYTRON_ENABLED);
final String authenticationContext = ModelNodeUtil.getResolvedStringIfSetOrGetDefault(operationContext, dataSourceNode, AUTHENTICATION_CONTEXT);

final Extension reauthPlugin = ModelNodeUtil.extractExtension(operationContext, dataSourceNode, REAUTH_PLUGIN_CLASSNAME, REAUTHPLUGIN_PROPERTIES);

final DsSecurity security = new DsSecurityImpl(username, password,
elytronEnabled? authenticationContext: securityDomain, elytronEnabled,reauthPlugin);
elytronEnabled? authenticationContext: securityDomain, elytronEnabled,reauthPlugin, credentialSourceSupplier);

final Boolean sharePreparedStatements = dataSourceNode.hasDefined(SHARE_PREPARED_STATEMENTS.getName()) ? dataSourceNode.get(
SHARE_PREPARED_STATEMENTS.getName()).asBoolean() : Defaults.SHARE_PREPARED_STATEMENTS;
Expand Down
Expand Up @@ -281,7 +281,7 @@ public static class DatasourceServerSetupTask implements ServerSetupTask {
public void setup(ManagementClient managementClient, String s) throws Exception {
h2Server = Server.createTcpServer("-tcpAllowOthers").start();
// open connection to database, because that's only (easy) way to set password for user sa
connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", DATABASE_PASSWORD);
connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", "sa", DATABASE_PASSWORD);
}

@Override
Expand Down
Expand Up @@ -58,7 +58,7 @@ public void tearDown(ManagementClient managementClient, String s) throws Excepti

private void createCredentialStore(final ModelControllerClient client) throws IOException {
final ModelNode addOperation = Operations.createAddOperation(CREDENTIAL_STORE_ADDRESS.toModelNode());
addOperation.get(URI).set("cr-store://test/store001.jceks?create.storage=true");
addOperation.get(URI).set("cr-store://test/store001.jceks?create=true;keyStoreType=JCEKS;modifiable=true");
addOperation.get(RELATIVE_TO).set("jboss.server.data.dir");
final ModelNode credentialReference = addOperation.get(CREDENTIAL_REFERENCE).setEmptyObject();
credentialReference.get("clear-text").set("joshua");
Expand Down

0 comments on commit 17ce9af

Please sign in to comment.