Skip to content

Commit

Permalink
[WFCORE-4750] Using regex for role in Elytron
Browse files Browse the repository at this point in the history
  • Loading branch information
Skyllarr committed Apr 24, 2020
1 parent f3a2e23 commit aa94054
Show file tree
Hide file tree
Showing 20 changed files with 447 additions and 59 deletions.
Expand Up @@ -256,6 +256,7 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration
resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_ROLE_MAPPER, ROLE_MAPPER_RUNTIME_CAPABILITY));
resourceRegistration.registerSubModel(RoleMapperDefinitions.getLogicalRoleMapperDefinition());
resourceRegistration.registerSubModel(RoleMapperDefinitions.getMappedRoleMapperDefinition());
resourceRegistration.registerSubModel(RoleMapperDefinitions.getRegexRoleMapperDefinition());

// Evidence Decoders
resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getX500SubjectEvidenceDecoderDefinition());
Expand Down
Expand Up @@ -430,6 +430,7 @@ interface ElytronDescriptionConstants {
String REFERRAL_MODE = "referral-mode";
String REGISTER_JASPI_FACTORY = "register-jaspi-factory";
String REGEX_PRINCIPAL_TRANSFORMER = "regex-principal-transformer";
String REGEX_ROLE_MAPPER = "regex-role-mapper";
String REGEX_VALIDATING_PRINCIPAL_TRANSFORMER = "regex-validating-principal-transformer";
String RELATIVE_TO = "relative-to";
String REMOVE_ALIAS = "remove-alias";
Expand Down
Expand Up @@ -137,6 +137,7 @@ private static void from10(ChainedTransformationDescriptionBuilder chainedBuilde
.getAttributeBuilder()
.addRejectCheck(REJECT_CREDENTIAL_REFERENCE_WITH_BOTH_STORE_AND_CLEAR_TEXT, CREDENTIAL_REFERENCE)
.end();
builder.rejectChildResource(PathElement.pathElement(ElytronDescriptionConstants.REGEX_ROLE_MAPPER));

}

Expand Down
Expand Up @@ -205,6 +205,13 @@ public void marshallSingleElement(AttributeDefinition attribute, ModelNode prope
})
.build();

private PersistentResourceXMLDescription regexRoleMapperParser = PersistentResourceXMLDescription.builder(RoleMapperDefinitions.getRegexRoleMapperDefinition().getPathElement())
.addAttribute(RoleMapperDefinitions.PATTERN)
.addAttribute(RoleMapperDefinitions.REPLACEMENT)
.addAttribute(RoleMapperDefinitions.KEEP_NON_MAPPED)
.addAttribute(RoleMapperDefinitions.REPLACE_ALL)
.build();

private PersistentResourceXMLDescription addPrefixRoleMapperParser = PersistentResourceXMLDescription.builder(RoleMapperDefinitions.getAddPrefixRoleMapperDefinition().getPathElement())
.addAttribute(RoleMapperDefinitions.PREFIX)
.build();
Expand Down Expand Up @@ -380,6 +387,7 @@ public PersistentResourceXMLDescription getParser() {
.addChild(x509SubjectAltNameEvidenceDecoder) // new
.addChild(getCustomComponentParser(CUSTOM_EVIDENCE_DECODER)) // new
.addChild(aggregateEvidenceDecoderParser) // new
.addChild(regexRoleMapperParser) // new
.build();
}
}
Expand Up @@ -37,7 +37,7 @@ class RegexAttributeDefinitions {

static final SimpleAttributeDefinition PATTERN = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PATTERN, ModelType.STRING, false)
.setAllowExpression(true)
.setValidator(new RexExValidator())
.setValidator(new RegExValidator())
.setMinSize(1)
.setRestartAllServices()
.build();
Expand All @@ -49,9 +49,9 @@ class RegexAttributeDefinitions {
.setRestartAllServices()
.build();

private static class RexExValidator extends StringLengthValidator {
private static class RegExValidator extends StringLengthValidator {

private RexExValidator() {
private RegExValidator() {
super(1, false, false);
}

Expand All @@ -70,7 +70,7 @@ public void validateParameter(String parameterName, ModelNode value) throws Oper

}

private static class CaptureGroupRexExValidator extends RexExValidator {
private static class CaptureGroupRexExValidator extends RegExValidator {

@Override
public void validateParameter(String parameterName, ModelNode value) throws OperationFailedException {
Expand Down
Expand Up @@ -58,6 +58,7 @@
import org.jboss.msc.value.InjectedValue;
import org.wildfly.extension.elytron.TrivialService.ValueSupplier;
import org.wildfly.security.authz.MappedRoleMapper;
import org.wildfly.security.authz.RegexRoleMapper;
import org.wildfly.security.authz.RoleMapper;
import org.wildfly.security.authz.Roles;

Expand All @@ -80,6 +81,14 @@ class RoleMapperDefinitions {
.setRestartAllServices()
.build();

static final SimpleAttributeDefinition PATTERN = new SimpleAttributeDefinitionBuilder(RegexAttributeDefinitions.PATTERN).build();

static final SimpleAttributeDefinition REPLACEMENT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.REPLACEMENT, ModelType.STRING, false)
.setAllowExpression(true)
.setMinSize(1)
.setRestartAllServices()
.build();

static final SimpleAttributeDefinition LEFT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.LEFT, ModelType.STRING, true)
.setMinSize(1)
.setRestartAllServices()
Expand Down Expand Up @@ -118,6 +127,12 @@ class RoleMapperDefinitions {
.setRestartAllServices()
.build();

static final SimpleAttributeDefinition REPLACE_ALL = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.REPLACE_ALL, ModelType.BOOLEAN, true)
.setAllowExpression(true)
.setDefaultValue(ModelNode.FALSE)
.setRestartAllServices()
.build();

static final StringListAttributeDefinition ROLES = new StringListAttributeDefinition.Builder(ElytronDescriptionConstants.ROLES)
.setAllowExpression(true)
.setMinSize(1)
Expand Down Expand Up @@ -173,6 +188,31 @@ protected ValueSupplier<RoleMapper> getValueSupplier(OperationContext context, M
return new RoleMapperResourceDefinition(ElytronDescriptionConstants.MAPPED_ROLE_MAPPER, add, ROLE_MAPPING_MAP, KEEP_MAPPED, KEEP_NON_MAPPED);
}

static ResourceDefinition getRegexRoleMapperDefinition() {
AbstractAddStepHandler add = new RoleMapperAddHandler(PATTERN, REPLACEMENT, KEEP_NON_MAPPED, REPLACE_ALL) {

@Override
protected ValueSupplier<RoleMapper> getValueSupplier(OperationContext context, ModelNode model) throws OperationFailedException {
final String regex = PATTERN.resolveModelAttribute(context, model).asString();
final String replacement = REPLACEMENT.resolveModelAttribute(context, model).asString();
final Boolean keepNonMapped = KEEP_NON_MAPPED.resolveModelAttribute(context, model).asBoolean();
final Boolean replaceAll = REPLACE_ALL.resolveModelAttribute(context, model).asBoolean();

final RegexRoleMapper roleMapper = new RegexRoleMapper.Builder()
.setPattern(regex)
.setReplacement(replacement)
.setKeepNonMapped(keepNonMapped)
.setReplaceAll(replaceAll)
.build();

return () -> roleMapper;

}
};

return new RoleMapperResourceDefinition(ElytronDescriptionConstants.REGEX_ROLE_MAPPER, add, PATTERN, REPLACEMENT, KEEP_NON_MAPPED, REPLACE_ALL);
}

static AggregateComponentDefinition<RoleMapper> getAggregateRoleMapperDefinition() {
return AGGREGATE_ROLE_MAPPER;
}
Expand Down
Expand Up @@ -578,4 +578,7 @@ public interface ElytronSubsystemMessages extends BasicLogger {
@Message(id = 1066, value = "Invalid value for cipher-suite-names. %s")
OperationFailedException invalidCipherSuiteNames(@Cause Throwable cause, String causeMessage);

@Message(id = 1067, value = "Value '%s' is not valid regex.")
OperationFailedException invalidRegex(String regex);

}
Expand Up @@ -668,6 +668,16 @@ elytron.mapped-role-mapper.keep-mapped=When set to 'true' the mapped roles will
elytron.mapped-role-mapper.keep-non-mapped=When set to 'true' the mapped roles will retain all roles, that have no defined mappings.
elytron.mapped-role-mapper.role-map=A string to string list map for mapping roles.

elytron.regex-role-mapper=A RoleMapper definition for a RoleMapper that performs a mapping based on regex and replaces matching roles with replacement pattern.
# Operations
elytron.regex-role-mapper.add=The add operation for the role mapper.
elytron.regex-role-mapper.remove=The remove operation for the role mapper.
# Attributes
elytron.regex-role-mapper.pattern=Regex string that will be used for matching. Regex can capture groups. Role matches the pattern if given pattern can be found in any substring of given role.
elytron.regex-role-mapper.replacement=Replacement that will be used when mapping roles that contain the pattern. Can make use of captured groups from pattern.
elytron.regex-role-mapper.keep-non-mapped=When set to 'true' then the roles that did not match the pattern will be kept and not removed.
elytron.regex-role-mapper.replace-all=When set to 'false', only first occurrence of the pattern will be replaced in role. When set to 'true' then all of the occurrences will be replaced by replacement.

#####################
# Realm Definitions #
#####################
Expand Down
42 changes: 42 additions & 0 deletions elytron/src/main/resources/schema/wildfly-elytron_10_0.xsd
Expand Up @@ -2522,6 +2522,7 @@
<xs:element name="x509-subject-alt-name-evidence-decoder" type="x509SubjectAltNameEvidenceDecoderType" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="custom-evidence-decoder" type="customEvidenceDecoderType" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="aggregate-evidence-decoder" type="aggregateEvidenceDecoderType" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="regex-role-mapper" type="regexRoleMapperType" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
</xs:complexType>

Expand Down Expand Up @@ -3542,6 +3543,47 @@
</xs:complexContent>
</xs:complexType>

<xs:complexType name="regexRoleMapperType">
<xs:annotation>
<xs:documentation>
A RoleMapper definition that uses pattern to find matching roles and then replaces these roles with replacement pattern.
Role matches the pattern in given pattern can be found in any substring of the role name.
</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="roleMapperType">
<xs:attribute name="pattern" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>
The pattern used for matching. Can capture groups.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="replacement" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>
The replacement string. Can make use of captured groups.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="keep-non-mapped" type="xs:boolean" use="optional" default="false">
<xs:annotation>
<xs:documentation>
If true, keep roles that did not match the provided pattern.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="replace-all" type="xs:boolean" use="optional" default="false">
<xs:annotation>
<xs:documentation>
If true, replace all occurrences of pattern and not only the first one.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:complexType name="roleMapperRefType">
<xs:annotation>
<xs:documentation>
Expand Down

0 comments on commit aa94054

Please sign in to comment.