@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Test realm</realm-name>
</login-config>

<security-constraint>
<web-resource-collection>
<web-resource-name>secured-area</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>

<security-role>
<role-name>*</role-name>
</security-role>
</web-app>
@@ -553,7 +553,7 @@ public static String makeCallWithBasicAuthn(URL url, String user, String pass, i
assertEquals("Unexpected status code returned after the authentication.", expectedStatusCode, statusCode);

if (checkFollowupAuthState) {
// Let's disable authentication for this client as we already have all the context neccessary to be
// Let's disable authentication for this client as we already have all the context necessary to be
// authorized (we expect that gained 'nonce' value can be re-used in our case here).
// By disabling authentication we simply get first server response and thus we can check whether we've
// got 200 OK or different response code.
@@ -0,0 +1,67 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wildfly.test.security.common.elytron;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.management.util.CLIWrapper;
import org.jboss.as.test.integration.security.common.Utils;
import org.jboss.dmr.ModelNode;

/**
* A {@link ConfigurableElement} to define a constant realm mapper resource.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
*/
public class ConstantRealmMapper implements ConfigurableElement {

private final PathAddress address;
private final String name;
private final String realm;

ConstantRealmMapper(String name, String realm) {
this.name = name;
this.address = PathAddress.pathAddress(PathElement.pathElement("subsystem", "elytron"), PathElement.pathElement("constant-realm-mapper", name));
this.realm = realm;
}

@Override
public String getName() {
return name;
}

@Override
public void create(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode addOperation = Util.createAddOperation(address);
addOperation.get("realm-name").set(realm);
Utils.applyUpdate(addOperation, client);
}

@Override
public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode removeOperation = Util.createRemoveOperation(address);
Utils.applyUpdate(removeOperation, client);
}

public static ConfigurableElement newInstance(final String name, final String realm) {
return new ConstantRealmMapper(name, realm);
}


}
@@ -0,0 +1,166 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wildfly.test.security.common.elytron;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.management.util.CLIWrapper;
import org.jboss.as.test.integration.security.common.Utils;
import org.jboss.dmr.ModelNode;

/**
* A {@link ConfigurableElement} to define a JDBC SecurityRealm resource.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
*/
public class JdbcSecurityRealm implements SecurityRealm {

private final PathAddress address;
private final String name;
private final List<ModelNode> principalQueries;

JdbcSecurityRealm(final String name, final List<ModelNode> principalQueries) {
this.name = name;
this.address = PathAddress.pathAddress(PathElement.pathElement("subsystem", "elytron"), PathElement.pathElement("jdbc-realm", name));
this.principalQueries = principalQueries;
}

@Override
public String getName() {
return name;
}

public ModelNode getAddOperation() {
ModelNode addOperation = Util.createAddOperation(address);
addOperation.get("principal-query").set(principalQueries);

return addOperation;
}

public ModelNode getRemoveOperation() {
return Util.createRemoveOperation(address);
}

@Override
public void create(ModelControllerClient client, CLIWrapper cli) throws Exception {
Utils.applyUpdate(getAddOperation(), client);
}

@Override
public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception {
Utils.applyUpdate(getRemoveOperation(), client);
}

public static Builder builder(final String name) {
return new Builder(name);
}

public static class PrincipalQueryBuilder {

private final Builder builder;
private final String datasource;
private final String sql;

private Map<String, ModelNode> passwordMappers = new LinkedHashMap<>();
private List<ModelNode> attributeMappers = new ArrayList<>();

PrincipalQueryBuilder(final Builder builder, final String datasource, final String sql) {
this.builder = builder;
this.datasource = datasource;
this.sql = sql;
}

public PrincipalQueryBuilder withPasswordMapper(final String passwordType, final String algorithm, final int passwordIndex, final int saltIndex, final int iteractionCountIndex) {
ModelNode passwordMapper = new ModelNode();
if (algorithm != null) {
passwordMapper.get("algorithm").set(algorithm);
}
passwordMapper.get("password-index").set(passwordIndex);
if (saltIndex > 0) {
passwordMapper.get("salt-index").set(saltIndex);
}
if (iteractionCountIndex > 0) {
passwordMapper.get("iteration-count-index").set(iteractionCountIndex);
}
passwordMappers.put(passwordType, passwordMapper);

return this;
}

public PrincipalQueryBuilder withAttributeMapper(final String attributeName, final int attributeIndex) {
ModelNode attributeMapper = new ModelNode();
attributeMapper.get("index").set(attributeIndex);
attributeMapper.get("to").set(attributeName);
attributeMappers.add(attributeMapper);

return this;
}


public Builder build() {
ModelNode principalQuery = new ModelNode();
principalQuery.get("data-source").set(datasource);
principalQuery.get("sql").set(sql);
for (Entry<String, ModelNode> mapper : passwordMappers.entrySet()) {
principalQuery.get(mapper.getKey()).set(mapper.getValue());
}
if (attributeMappers.size() > 0) {
principalQuery.get("attribute-mapping").set(attributeMappers);
}

return builder.addPrincipalQuery(principalQuery);
}

}

public static class Builder {

private final String name;
private List<ModelNode> queries = new ArrayList<>();

Builder(final String name) {
this.name = name;
}

public PrincipalQueryBuilder withPrincipalQuery(final String datasource, final String sql) {
return new PrincipalQueryBuilder(this, datasource, sql);
}

Builder addPrincipalQuery(final ModelNode principalQuery) {
queries.add(principalQuery);

return this;
}

public SecurityRealm build() {
return new JdbcSecurityRealm(name, queries);
}

}




}
@@ -0,0 +1,117 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wildfly.test.security.common.elytron;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.management.util.CLIWrapper;
import org.jboss.as.test.integration.security.common.Utils;
import org.jboss.dmr.ModelNode;

/**
* A {@link ConfigurableElement} to create a mapped regex realm mapper resource.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
*/
public class MappedRegexRealmMapper implements ConfigurableElement {

private final PathAddress address;
private final String name;
private final String pattern;
private final String delegateRealmMapper;
private final Map<String, String> realmMapping;

MappedRegexRealmMapper(final String name, final String pattern, final String delegateRealmMapper, final Map<String, String> realmMapping) {
this.name = name;
this.address = PathAddress.pathAddress(PathElement.pathElement("subsystem", "elytron"), PathElement.pathElement("mapped-regex-realm-mapper", name));
this.pattern = pattern;
this.delegateRealmMapper = delegateRealmMapper;
this.realmMapping = realmMapping;
}

@Override
public String getName() {
return name;
}

@Override
public void create(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode addOperation = Util.createAddOperation(address);
addOperation.get("pattern").set(pattern);
if (delegateRealmMapper != null) {
addOperation.get("delegate-realm-mapper").set(delegateRealmMapper);
}
if (realmMapping.size() > 0) {
ModelNode realmMapping = new ModelNode();
for (Entry<String, String> entry : this.realmMapping.entrySet()) {
realmMapping.get(entry.getKey()).set(entry.getValue());
}
addOperation.get("realm-map").set(realmMapping);
}
Utils.applyUpdate(addOperation, client);
}

@Override
public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode removeOperation = Util.createRemoveOperation(address);
Utils.applyUpdate(removeOperation, client);
}

public static Builder builder(final String name) {
return new Builder(name);
}

public static class Builder {

private final String name;
private String pattern;
private String delegateRealmMapper;
private Map<String, String> realmMapping = new HashMap<>();

Builder(final String name) {
this.name = name;
}

public Builder withPattern(final String pattern) {
this.pattern = pattern;

return this;
}

public Builder withDelegateRealmMapper(final String delegateRealmMapper) {
this.delegateRealmMapper = delegateRealmMapper;

return this;
}

public Builder withRealmMapping(final String from, final String to) {
realmMapping.put(from, to);

return this;
}

public MappedRegexRealmMapper build() {
return new MappedRegexRealmMapper(name, pattern, delegateRealmMapper, realmMapping);
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wildfly.test.security.common.elytron;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.management.util.CLIWrapper;
import org.jboss.as.test.integration.security.common.Utils;
import org.jboss.dmr.ModelNode;

/**
* A {@link ConfigurableElement} to define a regex principal transformer.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
*/
public class RegexPrincipalTransformer implements ConfigurableElement {

private final PathAddress address;
private final String name;
private final String pattern;
private final String replacement;
private final boolean replaceAll;


RegexPrincipalTransformer(String name, String pattern, String replacement, boolean replaceAll) {
this.name = name;
this.address = PathAddress.pathAddress(PathElement.pathElement("subsystem", "elytron"), PathElement.pathElement("regex-principal-transformer", name));
this.pattern = pattern;
this.replacement = replacement;
this.replaceAll = replaceAll;
}

@Override
public String getName() {
return name;
}

@Override
public void create(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode addOperation = Util.createAddOperation(address);
addOperation.get("pattern").set(pattern);
addOperation.get("replacement").set(replacement);
addOperation.get("replace-all").set(replaceAll);
Utils.applyUpdate(addOperation, client);
}

@Override
public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode removeOperation = Util.createRemoveOperation(address);
Utils.applyUpdate(removeOperation, client);
}

public static Builder builder(final String name) {
return new Builder(name);
}

public static class Builder {

private final String name;
private String pattern;
private String replacement;
private boolean replaceAll;

Builder(final String name) {
this.name = name;
}

public Builder withPattern(final String pattern) {
this.pattern = pattern;

return this;
}

public Builder withReplacement(final String replacement) {
this.replacement = replacement;

return this;
}

public Builder replaceAll(final boolean replaceAll) {
this.replaceAll = replaceAll;

return this;
}

public RegexPrincipalTransformer build() {
return new RegexPrincipalTransformer(name, pattern, replacement, replaceAll);
}

}

}
@@ -71,7 +71,9 @@ private SimpleSecurityDomain(Builder builder) {
public void create(ModelControllerClient client, CLIWrapper cli) throws Exception {
ModelNode op = Util
.createAddOperation(PathAddress.pathAddress().append("subsystem", "elytron").append("security-domain", name));
op.get("default-realm").set(defaultRealm);
if (defaultRealm != null) {
op.get("default-realm").set(defaultRealm);
}
if (outflowAnonymous != null) {
op.get("outflow-anonymous").set(outflowAnonymous);
}