You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by km...@apache.org on 2015/07/31 03:12:21 UTC

knox git commit: KNOX-579: Regex based identity assertion provider with static dictionary lookup

Repository: knox
Updated Branches:
  refs/heads/master c414c58ad -> b618ff3e3


KNOX-579: Regex based identity assertion provider with static dictionary lookup


Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/b618ff3e
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/b618ff3e
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/b618ff3e

Branch: refs/heads/master
Commit: b618ff3e350eb24a0626b5ae90e1c246ce5e325b
Parents: c414c58
Author: Kevin Minder <ke...@hortonworks.com>
Authored: Thu Jul 30 21:12:14 2015 -0400
Committer: Kevin Minder <ke...@hortonworks.com>
Committed: Thu Jul 30 21:12:14 2015 -0400

----------------------------------------------------------------------
 CHANGES                                         |   1 +
 .../pom.xml                                     |  99 ++++++++++++++++++
 ...exIdentityAsserterDeploymentContributor.java |  33 ++++++
 .../filter/RegexIdentityAssertionFilter.java    |  86 ++++++++++++++++
 .../regex/filter/RegexTemplate.java             |  75 ++++++++++++++
 ...gateway.deploy.ProviderDeploymentContributor |  19 ++++
 ...entityAsserterDeploymentContributorTest.java |  44 ++++++++
 .../RegexIdentityAssertionFilterTest.java       | 103 +++++++++++++++++++
 .../regex/filter/RegexTemplateTest.java         |  72 +++++++++++++
 gateway-release/pom.xml                         |   4 +
 pom.xml                                         |   6 ++
 11 files changed, 542 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index af20c8a..fc68884 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,7 @@ Release Notes - Apache Knox - Version 0.7.0
     * [KNOX-547] - KnoxCLI adds new validate-topology and list-topologies commands.
     * [KNOX-548] - KnoxCLI adds a new system-user-auth-test command to test a topology's system username and password
     * [KNOX-549] - New Service-Test API can be added to topology. Accessible via Http call or KnoxCLI
+    * [KNOX-579] - Regex based identity assertion provider with static dictionary lookup
 
 ** Improvement
     * [KNOX-553] - Added topology validation from KnoxCLI to TopologyService deployment.

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/pom.xml b/gateway-provider-identity-assertion-regex/pom.xml
new file mode 100644
index 0000000..33f5289
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.knox</groupId>
+        <artifactId>gateway</artifactId>
+        <version>0.7.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>gateway-provider-identity-assertion-regex</artifactId>
+
+    <name>gateway-provider-identity-assertion-regex</name>
+    <description>An extension to the gateway that provides an easy integration point for asserting identity to Hadoop clusters using using some custom mapping facility.</description>
+
+    <licenses>
+        <license>
+            <name>The Apache Software License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${gateway-group}</groupId>
+            <artifactId>gateway-provider-identity-assertion-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>${gateway-group}</groupId>
+            <artifactId>gateway-spi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>${gateway-group}</groupId>
+            <artifactId>gateway-test-utils</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>test-jetty-servlet</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.xmlmatchers</groupId>
+            <artifactId>xml-matchers</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributor.java b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributor.java
new file mode 100644
index 0000000..6ce5676
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributor.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.hadoop.gateway.identityasserter.regex.filter;
+
+import org.apache.hadoop.gateway.identityasserter.common.filter.AbstractIdentityAsserterDeploymentContributor;
+
+public class RegexIdentityAsserterDeploymentContributor extends AbstractIdentityAsserterDeploymentContributor {
+
+  @Override
+  public String getName() {
+    return "Regex";
+  }
+
+  protected String getFilterClassname() {
+    return RegexIdentityAssertionFilter.class.getName();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
new file mode 100644
index 0000000..a9a71e5
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.hadoop.gateway.identityasserter.regex.filter;
+
+import javax.security.auth.Subject;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+
+import org.apache.hadoop.gateway.identityasserter.common.filter.CommonIdentityAssertionFilter;
+import org.apache.hadoop.gateway.security.principal.PrincipalMappingException;
+
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+public class RegexIdentityAssertionFilter extends CommonIdentityAssertionFilter {
+
+  private String input = null;
+  private String output = null;
+  private Map<String,String> dict;
+  RegexTemplate template;
+  
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    try {
+      input = filterConfig.getInitParameter( "input" );
+      if( input == null ) {
+        input = "";
+      }
+      output = filterConfig.getInitParameter( "output" );
+      if( output == null ) {
+        output = "";
+      }
+      dict = loadDictionary( filterConfig.getInitParameter( "lookup" ) );
+      template = new RegexTemplate( input, output, dict );
+    } catch ( PrincipalMappingException e ) {
+      throw new ServletException( e );
+    }
+  }
+
+  public String[] mapGroupPrincipals(String mappedPrincipalName, Subject subject) {
+    // Returning null will allow existing Subject group principals to remain the same
+    return null;
+  }
+
+  public String mapUserPrincipal(String principalName) {
+    return template.apply( principalName );
+  }
+
+  private Map<String, String> loadDictionary( String config ) throws PrincipalMappingException {
+    Map<String,String> dict = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    if( config != null && !config.isEmpty() ) {
+      try {
+        StringTokenizer t = new StringTokenizer( config, ";" );
+        while( t.hasMoreTokens() ) {
+          String nvp = t.nextToken();
+          String[] a = nvp.split( "=" );
+          dict.put( a[0].trim(), a[1].trim() );
+        }
+        return dict;
+      } catch( Exception e ) {
+        dict.clear();
+        throw new PrincipalMappingException(
+            "Unable to load lookup dictionary from provided configuration: " + config +
+                ".  No principal mapping will be provided.", e );
+      }
+    }
+    return dict;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplate.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplate.java b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplate.java
new file mode 100644
index 0000000..0a9912d
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplate.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.apache.hadoop.gateway.identityasserter.regex.filter;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexTemplate {
+
+  private static Pattern directPattern = Pattern.compile( "\\{(\\[?\\d+?\\]?)\\}" );
+  private static Pattern indirectPattern = Pattern.compile( "\\[(\\d+?)\\]" );
+
+  Pattern inputPattern;
+  String outputTemplate;
+  Map<String,String> lookupTable;
+
+  public RegexTemplate( String regex, String template ) {
+    this( regex, template, null );
+  }
+
+  public RegexTemplate( String regex, String template, Map<String,String> map ) {
+    this.inputPattern = Pattern.compile( regex );
+    this.outputTemplate = template;
+    this.lookupTable = map;
+  }
+
+  public String apply( String input ) {
+    String output = outputTemplate;
+    Matcher inputMatcher = inputPattern.matcher( input );
+    if( inputMatcher.find() ) {
+      output = expandTemplate( inputMatcher, output );
+    }
+    return output;
+  }
+
+  private String expandTemplate( Matcher inputMatcher, String output ) {
+    Matcher directMatcher = directPattern.matcher( output );
+    while( directMatcher.find() ) {
+      String lookupValue = null;
+      String lookupStr = directMatcher.group( 1 );
+      Matcher indirectMatcher = indirectPattern.matcher( lookupStr );
+      if( indirectMatcher.find() ) {
+        lookupStr = indirectMatcher.group( 1 );
+        int lookupIndex = Integer.parseInt( lookupStr );
+        if( lookupTable != null ) {
+          String lookupKey = inputMatcher.group( lookupIndex );
+          lookupValue = lookupTable.get( lookupKey );
+        }
+      } else {
+        int lookupIndex = Integer.parseInt( lookupStr );
+        lookupValue = inputMatcher.group( lookupIndex );
+      }
+      output = directMatcher.replaceFirst( lookupValue == null ? "" : lookupValue );
+      directMatcher = directPattern.matcher( output );
+    }
+    return output;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor b/gateway-provider-identity-assertion-regex/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
new file mode 100644
index 0000000..0dd2d2e
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
@@ -0,0 +1,19 @@
+##########################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+##########################################################################
+
+org.apache.hadoop.gateway.identityasserter.regex.filter.RegexIdentityAsserterDeploymentContributor
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributorTest.java b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributorTest.java
new file mode 100644
index 0000000..6d93359
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAsserterDeploymentContributorTest.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.hadoop.gateway.identityasserter.regex.filter;
+
+import org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor;
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+public class RegexIdentityAsserterDeploymentContributorTest {
+
+  @Test
+  public void testServiceLoader() throws Exception {
+    ServiceLoader<ProviderDeploymentContributor> loader = ServiceLoader.load( ProviderDeploymentContributor.class );
+    Iterator<ProviderDeploymentContributor> iterator = loader.iterator();
+    assertThat( "Service iterator empty.", iterator.hasNext() );
+    while( iterator.hasNext() ) {
+      Object object = iterator.next();
+      if( object instanceof RegexIdentityAsserterDeploymentContributor ) {
+        return;
+      }
+    }
+    fail( "Failed to find " + RegexIdentityAsserterDeploymentContributor.class.getName() + " via service loader." );
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilterTest.java b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilterTest.java
new file mode 100644
index 0000000..f5e623d
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilterTest.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.hadoop.gateway.identityasserter.regex.filter;
+
+import org.apache.hadoop.gateway.security.GroupPrincipal;
+import org.apache.hadoop.gateway.security.PrimaryPrincipal;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import javax.security.auth.Subject;
+import javax.servlet.FilterConfig;
+import java.security.Principal;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+public class RegexIdentityAssertionFilterTest {
+
+  @Test
+  public void testExtractUsernameFromEmail() throws Exception {
+    FilterConfig config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.replay( config );
+
+    RegexIdentityAssertionFilter filter = new RegexIdentityAssertionFilter();
+
+    Subject subject = new Subject();
+    subject.getPrincipals().add(new PrimaryPrincipal( "member@us.apache.org" ) );
+    subject.getPrincipals().add(new GroupPrincipal( "user" ) );
+    subject.getPrincipals().add( new GroupPrincipal( "admin" ) );
+
+    // First test is with no config.  Since the output template is the empty string that should be the result.
+    filter.init(config);
+    String actual = filter.mapUserPrincipal(((Principal) subject.getPrincipals(PrimaryPrincipal.class).toArray()[0]).getName());
+    String[] groups = filter.mapGroupPrincipals(actual, subject);
+    assertThat( actual, is( "" ) );
+    assertThat( groups, is( nullValue() ) ); // means for the caller to use the existing subject groups
+
+    // Test what is effectively a static mapping
+    config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.expect(config.getInitParameter( "output" ) ).andReturn( "test-output" ).anyTimes();
+    EasyMock.replay( config );
+    filter.init( config );
+    actual = filter.mapUserPrincipal(((Principal) subject.getPrincipals(PrimaryPrincipal.class).toArray()[0]).getName());
+    assertEquals( actual, "test-output" );
+
+    // Test username extraction.
+    config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.expect(config.getInitParameter( "input" ) ).andReturn( "(.*)@.*" ).anyTimes();
+    EasyMock.expect(config.getInitParameter( "output" ) ).andReturn( "prefix_{1}_suffix" ).anyTimes();
+    EasyMock.replay( config );
+    filter.init( config );
+    actual = filter.mapUserPrincipal( "member@us.apache.org" );
+    assertEquals( actual, "prefix_member_suffix" );
+
+  }
+
+  @Test
+  public void testMapDomain() throws Exception {
+    FilterConfig config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.replay( config );
+
+    RegexIdentityAssertionFilter filter = new RegexIdentityAssertionFilter();
+
+    Subject subject = new Subject();
+    subject.getPrincipals().add(new PrimaryPrincipal( "member@us.apache.org" ) );
+    subject.getPrincipals().add(new GroupPrincipal( "user" ) );
+    subject.getPrincipals().add( new GroupPrincipal( "admin" ) );
+
+    String actual;
+
+    // Test dictionary lookup.
+    config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.expect(config.getInitParameter( "input" ) ).andReturn( "(.*)@(.*?)\\..*" ).anyTimes();
+    EasyMock.expect(config.getInitParameter( "output" ) ).andReturn( "prefix_{1}_suffix:{[2]}" ).anyTimes();
+    EasyMock.expect(config.getInitParameter( "lookup" ) ).andReturn( "us=USA;ca=CANADA" ).anyTimes();
+    EasyMock.replay( config );
+    filter.init( config );
+    actual = filter.mapUserPrincipal( "member1@us.apache.org" );
+    assertThat( actual, is( "prefix_member1_suffix:USA" ) );
+    actual = filter.mapUserPrincipal( "member2@ca.apache.org" );
+    assertThat( actual, is( "prefix_member2_suffix:CANADA" ) );
+    actual = filter.mapUserPrincipal( "member3@nj.apache.org" );
+    assertThat( actual, is( "prefix_member3_suffix:" ) );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplateTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplateTest.java b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplateTest.java
new file mode 100644
index 0000000..b32cd41
--- /dev/null
+++ b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/hadoop/gateway/identityasserter/regex/filter/RegexTemplateTest.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.apache.hadoop.gateway.identityasserter.regex.filter;
+
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class RegexTemplateTest {
+
+  @Test
+  public void testExtractUsernameFromEmailAddress() {
+
+    RegexTemplate template;
+    String actual;
+
+    template = new RegexTemplate( "(.*)@.*", "prefix_{1}_suffix" );
+    actual = template.apply( "member@apache.org" );
+    assertThat( actual, is( "prefix_member_suffix" ) );
+
+    template = new RegexTemplate( "(.*)@.*", "prefix_{0}_suffix" );
+    actual = template.apply( "member@apache.org" );
+    assertThat( actual, is( "prefix_member@apache.org_suffix" ) );
+
+    template = new RegexTemplate( "(.*)@.*", "prefix_{1}_{a}_suffix" );
+    actual = template.apply( "member@apache.org" );
+    assertThat( actual, is( "prefix_member_{a}_suffix" ) );
+
+  }
+
+  @Test
+  public void testExtractUsernameFromEmailAddressAndMapDomain() {
+
+    RegexTemplate template;
+    Map<String,String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    map.put( "us", "USA" );
+    map.put( "ca", "CANADA" );
+
+    String actual;
+
+    template = new RegexTemplate( "(.*)@(.*?)\\..*", "prefix_{1}:{[2]}_suffix", map );
+    actual = template.apply( "member@us.apache.org" );
+    assertThat( actual, is( "prefix_member:USA_suffix" ) );
+
+    actual = template.apply( "member@ca.apache.org" );
+    assertThat( actual, is( "prefix_member:CANADA_suffix" ) );
+
+    actual = template.apply( "member@nj.apache.org" );
+    assertThat( actual, is( "prefix_member:_suffix" ) );
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/gateway-release/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-release/pom.xml b/gateway-release/pom.xml
index b95865f..3e87aac 100644
--- a/gateway-release/pom.xml
+++ b/gateway-release/pom.xml
@@ -244,6 +244,10 @@
         </dependency>
         <dependency>
             <groupId>${gateway-group}</groupId>
+            <artifactId>gateway-provider-identity-assertion-regex</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${gateway-group}</groupId>
             <artifactId>gateway-provider-ha</artifactId>
         </dependency>
 

http://git-wip-us.apache.org/repos/asf/knox/blob/b618ff3e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index cc66fab..50abb5f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@
         <module>gateway-provider-security-authz-acls</module>
         <module>gateway-provider-identity-assertion-common</module>
         <module>gateway-provider-identity-assertion-concat</module>
+        <module>gateway-provider-identity-assertion-regex</module>
         <module>gateway-provider-security-picketlink</module>
         <module>gateway-provider-identity-assertion-pseudo</module>
         <module>gateway-provider-jersey</module>
@@ -444,6 +445,11 @@
             </dependency>
             <dependency>
                 <groupId>${gateway-group}</groupId>
+                <artifactId>gateway-provider-identity-assertion-regex</artifactId>
+                <version>${gateway-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${gateway-group}</groupId>
                 <artifactId>gateway-provider-rewrite</artifactId>
                 <version>${gateway-version}</version>
             </dependency>