You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by sm...@apache.org on 2022/03/16 13:26:54 UTC

[knox] branch master updated: KNOX-2714 - Added doAs support to KnoxToken service (#545)

This is an automated email from the ASF dual-hosted git repository.

smolnar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 69a92c2  KNOX-2714 - Added doAs support to KnoxToken service (#545)
69a92c2 is described below

commit 69a92c2d33d467b855a397444a103e82f7e566de
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Wed Mar 16 14:26:46 2022 +0100

    KNOX-2714 - Added doAs support to KnoxToken service (#545)
---
 .../resources/applications/tokengen/app/index.html |  3 +
 .../applications/tokengen/app/js/tokengen.js       | 20 +++++-
 .../hadoopauth/filter/HadoopAuthFilter.java        | 70 ++++--------------
 .../jwt/filter/JWTAccessTokenAssertionFilter.java  | 11 +--
 .../jwt/filter/JWTAuthCodeAssertionFilter.java     |  2 +-
 ...okenIDAsHTTPBasicCredsFederationFilterTest.java | 18 ++++-
 gateway-release/home/conf/topologies/homepage.xml  | 12 ++++
 .../token/impl/DefaultTokenAuthorityService.java   |  2 +-
 .../token/impl/DefaultTokenStateService.java       | 18 ++++-
 .../services/token/impl/JDBCTokenStateService.java | 10 +++
 .../services/token/impl/TokenStateDatabase.java    | 15 +++-
 .../token/impl/TokenStateServiceMessages.java      |  3 +
 .../impl/DefaultTokenAuthorityServiceTest.java     | 42 +++++------
 .../gateway/service/knoxsso/WebSSOResource.java    |  2 +-
 .../service/knoxsso/WebSSOResourceTest.java        |  2 +-
 gateway-service-knoxtoken/pom.xml                  | 16 +++++
 .../gateway/service/knoxtoken/TokenResource.java   | 48 ++++++++++---
 .../knoxtoken/TokenServiceResourceTest.java        | 58 +++++++++++++--
 gateway-spi/pom.xml                                | 15 ++++
 .../services/security/token/JWTokenAttributes.java | 12 ++--
 .../security/token/JWTokenAttributesBuilder.java   | 15 ++--
 .../services/security/token/TokenMetadata.java     | 19 +++--
 .../services/security/token/TokenStateService.java |  9 +++
 .../apache/knox/gateway/util/AuthFilterUtils.java  | 82 ++++++++++++++++++++++
 .../app/token.management.component.html            | 51 ++++++++++++--
 .../app/token.management.component.ts              | 19 +++--
 .../app/token.management.service.ts                |  6 +-
 27 files changed, 434 insertions(+), 146 deletions(-)

diff --git a/gateway-applications/src/main/resources/applications/tokengen/app/index.html b/gateway-applications/src/main/resources/applications/tokengen/app/index.html
index dbde759..3810a8d 100644
--- a/gateway-applications/src/main/resources/applications/tokengen/app/index.html
+++ b/gateway-applications/src/main/resources/applications/tokengen/app/index.html
@@ -79,6 +79,9 @@
                             </table>
                             <label style="display: none; color: red;" id="invalidLifetimeText"><i class="icon-warning"></i>Invalid lifetime!</label>
                         </div>
+                        <label><i class="icon-user"></i> Generating token for (impersonation):</label>
+                        <input type="text" name="doas" id="doas" size="50" maxlength="255">
+                        <label style="display: none; color: red;" id="invalidDoasText"><i class="icon-warning"></i>Invalid doAs!</label>
                     </div>
                     <span id="errorBox" class="help-inline" style="color:red;display:none;"><span class="errorMsg"></span>
                         <i class="icon-warning-sign" style="color:#ae2817;"></i>
diff --git a/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js b/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js
index 09b2521..6aed159 100644
--- a/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js
+++ b/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js
@@ -158,6 +158,19 @@ function validateComment(comment) {
     return valid;
 }
 
+function validateDoAs(doAs) {
+    var valid = true;
+    if (doAs.value != '') {
+        doAs.reportValidity();
+        valid = doAs.checkValidity();
+        if (!valid) {
+            $('#invalidDoasText').show();
+        }
+    }
+
+    return valid;
+}
+
 function maximumLifetimeExceeded(maximumLifetime, days, hours, mins) {
 	if (maximumLifetime == -1) {
 		return false;
@@ -196,6 +209,11 @@ var gen = function() {
         if (form.comment.value != '') {
             params = params + (lifespanInputEnabled === "true" ? "&" : "?") + 'comment=' + encodeURIComponent(form.comment.value);
         }
+
+        if (form.doas.value != '') {
+            params = params + (lifespanInputEnabled === "true" || form.comment.value != '' ? "&" : "?") + 'doAs=' + encodeURIComponent(form.doas.value);
+        }
+
         var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
         request.open("GET", apiUrl + params, true);
         request.send(null);
@@ -235,7 +253,7 @@ var gen = function() {
         }
     }
 
-    if (validateLifespan(lifespanInputEnabled, form.lt_days, form.lt_hours, form.lt_mins) && validateComment(form.comment)) {
+    if (validateLifespan(lifespanInputEnabled, form.lt_days, form.lt_hours, form.lt_mins) && validateComment(form.comment) && validateDoAs(form.doas)) {
         if (maximumLifetimeExceeded(form.maximumLifetimeSeconds.textContent, lt_days, lt_hours, lt_mins)) {
             swal({
                 title: "Warning",
diff --git a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
index 1e23167..a12a21b 100755
--- a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
+++ b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
@@ -18,7 +18,6 @@
 package org.apache.knox.gateway.hadoopauth.filter;
 
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.AuthorizationException;
 import org.apache.hadoop.security.authorize.ProxyUsers;
 import org.apache.hadoop.util.HttpExceptionUtils;
@@ -115,7 +114,10 @@ public class HadoopAuthFilter extends
 
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
-    Configuration conf = getProxyuserConfiguration(filterConfig);
+    // Return a {@link Configuration} instance with the proxy user
+    // (<code>hadoop.proxyuser.*</code>) properties set using parameter information
+    // from the filterConfig.
+    final Configuration conf = AuthFilterUtils.getProxyUserConfiguration(filterConfig, PROXYUSER_PREFIX);
     ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX);
 
     Collection<String> ignoredServices = null;
@@ -198,38 +200,18 @@ public class HadoopAuthFilter extends
      * (proxy user) is allowed to set specified proxied user. It is expected that the relevant
      * topology file has the required hadoop.proxyuser configurations set.
      */
-    if (!ignoreDoAs(request.getRemoteUser())) {
-      String doAsUser = request.getParameter(QUERY_PARAMETER_DOAS);
-      if (doAsUser != null && !doAsUser.equals(request.getRemoteUser())) {
-        LOG.hadoopAuthDoAsUser(doAsUser, request.getRemoteUser(), request.getRemoteAddr());
-
-        UserGroupInformation requestUgi = (request.getUserPrincipal() != null)
-            ? UserGroupInformation.createRemoteUser(request.getRemoteUser())
-            : null;
-
-        if (requestUgi != null) {
-          requestUgi = UserGroupInformation.createProxyUser(doAsUser, requestUgi);
-
+    HttpServletRequest proxyRequest = null;
+    final String remoteUser = request.getRemoteUser();
+    if (!ignoreDoAs(remoteUser)) {
+      final String doAsUser = request.getParameter(QUERY_PARAMETER_DOAS);
+      if (doAsUser != null && !doAsUser.equals(remoteUser)) {
+        LOG.hadoopAuthDoAsUser(doAsUser, remoteUser, request.getRemoteAddr());
+        if (request.getUserPrincipal() != null) {
           try {
-            ProxyUsers.authorize(requestUgi, request.getRemoteAddr());
-
-            final UserGroupInformation ugiF = requestUgi;
-            request = new HttpServletRequestWrapper(request) {
-              @Override
-              public String getRemoteUser() {
-                return ugiF.getShortUserName();
-              }
-
-              @Override
-              public Principal getUserPrincipal() {
-                return ugiF::getUserName;
-              }
-            };
-
+            proxyRequest = AuthFilterUtils.getProxyRequest(request, doAsUser);
             LOG.hadoopAuthProxyUserSuccess();
           } catch (AuthorizationException ex) {
-            HttpExceptionUtils.createServletExceptionResponse(response,
-                HttpServletResponse.SC_FORBIDDEN, ex);
+            HttpExceptionUtils.createServletExceptionResponse(response, HttpServletResponse.SC_FORBIDDEN, ex);
             LOG.hadoopAuthProxyUserFailed(ex);
             return;
           }
@@ -237,7 +219,7 @@ public class HadoopAuthFilter extends
       }
     }
 
-    super.doFilter(filterChain, request, response);
+    super.doFilter(filterChain, proxyRequest == null ? request : proxyRequest, response);
   }
 
   /**
@@ -327,30 +309,6 @@ public class HadoopAuthFilter extends
     return (userName == null) || userName.isEmpty() || ignoreDoAs.contains(userName.toLowerCase(Locale.ROOT));
   }
 
-  /**
-   * Return a {@link Configuration} instance with the proxy user (<code>hadoop.proxyuser.*</code>)
-   * properties set using parameter information from the filterConfig.
-   *
-   * @param filterConfig the {@link FilterConfig} to query
-   * @return a {@link Configuration}
-   */
-  private Configuration getProxyuserConfiguration(FilterConfig filterConfig) {
-    Configuration conf = new Configuration(false);
-
-    // Iterate through the init parameters of the filter configuration to add Hadoop proxyuser
-    // parameters to the configuration instance
-    Enumeration<?> names = filterConfig.getInitParameterNames();
-    while (names.hasMoreElements()) {
-      String name = (String) names.nextElement();
-      if (name.startsWith(PROXYUSER_PREFIX + ".")) {
-        String value = filterConfig.getInitParameter(name);
-        conf.set(name, value);
-      }
-    }
-
-    return conf;
-  }
-
   // Visible for testing
   Properties getConfiguration(AliasService aliasService, String configPrefix, FilterConfig filterConfig) throws ServletException {
     final Properties props = new Properties();
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAccessTokenAssertionFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAccessTokenAssertionFilter.java
index 37644d0..2bfcd9d 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAccessTokenAssertionFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAccessTokenAssertionFilter.java
@@ -19,7 +19,6 @@ package org.apache.knox.gateway.provider.federation.jwt.filter;
 
 import java.io.IOException;
 import java.security.AccessController;
-import java.security.Principal;
 import java.text.ParseException;
 import java.util.HashMap;
 
@@ -35,8 +34,8 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.knox.gateway.filter.security.AbstractIdentityAssertionFilter;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.provider.federation.jwt.JWTMessages;
-import org.apache.knox.gateway.services.ServiceType;
 import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.ServiceType;
 import org.apache.knox.gateway.services.registry.ServiceRegistry;
 import org.apache.knox.gateway.services.security.token.JWTokenAttributes;
 import org.apache.knox.gateway.services.security.token.JWTokenAttributesBuilder;
@@ -140,15 +139,9 @@ public class JWTAccessTokenAssertionFilter extends AbstractIdentityAssertionFilt
   private String getAccessToken(final String principalName, String serviceName, long expires) {
     String accessToken = null;
 
-    Principal p = new Principal() {
-      @Override
-      public String getName() {
-        return principalName;
-      }
-    };
     JWT token;
     try {
-      final JWTokenAttributes jwtAttributes = new JWTokenAttributesBuilder().setPrincipal(p).setAudiences(serviceName).setAlgorithm(signatureAlgorithm).setExpires(expires).build();
+      final JWTokenAttributes jwtAttributes = new JWTokenAttributesBuilder().setUserName(principalName).setAudiences(serviceName).setAlgorithm(signatureAlgorithm).setExpires(expires).build();
       token = authority.issueToken(jwtAttributes);
       // Coverity CID 1327961
       if( token != null ) {
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
index ab03f42..7bd3b55 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
@@ -65,7 +65,7 @@ public class JWTAuthCodeAssertionFilter extends AbstractIdentityAssertionFilter
     principalName = mapper.mapUserPrincipal(principalName);
     JWT authCode;
     try {
-      authCode = authority.issueToken(new JWTokenAttributesBuilder().setPrincipal(subject).setAlgorithm(signatureAlgorithm).build());
+      authCode = authority.issueToken(new JWTokenAttributesBuilder().setUserName(principalName).setAlgorithm(signatureAlgorithm).build());
       // get the url for the token service
       String url = null;
       if (sr != null) {
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java
index 322debd..7a15255 100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java
@@ -31,6 +31,7 @@ import java.util.Properties;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -487,8 +488,23 @@ public class TokenIDAsHTTPBasicCredsFederationFilterTest extends JWTAsHTTPBasicC
 
         @Override
         public Collection<KnoxToken> getTokens(String userName) {
+          return fetchTokens(userName, false);
+        }
+
+        @Override
+        public Collection<KnoxToken> getDoAsTokens(String createdBy) {
+          return fetchTokens(createdBy, true);
+        }
+
+        private Collection<KnoxToken> fetchTokens(String userName, boolean createdBy) {
           final Collection<KnoxToken> tokens = new TreeSet<>();
-          tokenMetadata.entrySet().stream().filter(entry -> entry.getValue().getUserName().equals(userName)).forEach(metadata -> {
+          final Predicate<Map.Entry<String, TokenMetadata>> filterPredicate;
+          if (createdBy) {
+            filterPredicate = entry -> userName.equals(entry.getValue().getCreatedBy());
+          } else {
+            filterPredicate = entry -> userName.equals(entry.getValue().getUserName());
+          }
+          tokenMetadata.entrySet().stream().filter(filterPredicate).forEach(metadata -> {
             String tokenId = metadata.getKey();
             try {
               tokens.add(new KnoxToken(tokenId, getTokenIssueTime(tokenId), getTokenExpiration(tokenId), 0L, metadata.getValue()));
diff --git a/gateway-release/home/conf/topologies/homepage.xml b/gateway-release/home/conf/topologies/homepage.xml
index b70d34e..a3fce65 100644
--- a/gateway-release/home/conf/topologies/homepage.xml
+++ b/gateway-release/home/conf/topologies/homepage.xml
@@ -101,6 +101,18 @@
           <name>knox.token.type</name>
           <value>JWT</value>
       </param>
+      <param>
+          <name>knox.token.proxyuser.admin.users</name>
+          <value>*</value>
+      </param>
+      <param>
+          <name>knox.token.proxyuser.admin.groups</name>
+          <value>*</value>
+      </param>
+      <param>
+          <name>knox.token.proxyuser.admin.hosts</name>
+          <value>*</value>
+      </param>
    </service>
    <application>
       <name>tokengen</name>
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
index 7c5c295..6ab7342 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
@@ -106,7 +106,7 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
   public JWT issueToken(JWTokenAttributes jwtAttributes) throws TokenServiceException {
     String[] claimArray = new String[6];
     claimArray[0] = "KNOXSSO";
-    claimArray[1] = jwtAttributes.getPrincipal().getName();
+    claimArray[1] = jwtAttributes.getUserName();
     claimArray[2] = null;
     if (jwtAttributes.getExpires() == -1) {
       claimArray[3] = null;
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java
index 3096910..cfe9a4a 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java
@@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import javax.management.InstanceAlreadyExistsException;
@@ -420,8 +421,23 @@ public class DefaultTokenStateService implements TokenStateService {
 
   @Override
   public Collection<KnoxToken> getTokens(String userName) {
+    return fetchTokens(userName, false);
+  }
+
+  @Override
+  public Collection<KnoxToken> getDoAsTokens(String createdBy) {
+    return fetchTokens(createdBy, true);
+  }
+
+  private Collection<KnoxToken> fetchTokens(String userName, boolean createdBy) {
     final Collection<KnoxToken> tokens = new TreeSet<>();
-    metadataMap.entrySet().stream().filter(entry -> entry.getValue().getUserName().equals(userName)).forEach(metadata -> {
+    final Predicate<Map.Entry<String, TokenMetadata>> filterPredicate;
+    if (createdBy) {
+      filterPredicate = entry -> userName.equals(entry.getValue().getCreatedBy());
+    } else {
+      filterPredicate = entry -> userName.equals(entry.getValue().getUserName());
+    }
+    metadataMap.entrySet().stream().filter(filterPredicate).forEach(metadata -> {
       String tokenId = metadata.getKey();
       try {
         tokens.add(new KnoxToken(tokenId, getTokenIssueTime(tokenId), getTokenExpiration(tokenId), getMaxLifetime(tokenId), metadata.getValue()));
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
index 13f9a1d..6b27ec5 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
@@ -303,4 +303,14 @@ public class JDBCTokenStateService extends DefaultTokenStateService {
       return Collections.emptyList();
     }
   }
+
+  @Override
+  public Collection<KnoxToken> getDoAsTokens(String createdBy) {
+    try {
+      return tokenDatabase.getDoAsTokens(createdBy);
+    } catch (SQLException e) {
+      log.errorFetchingDoAsTokensForUserFromDatabase(createdBy, e.getMessage(), e);
+      return Collections.emptyList();
+    }
+  }
 }
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
index 73b3982..0b70514 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
@@ -59,6 +59,9 @@ public class TokenStateDatabase {
   private static final String GET_TOKENS_BY_USER_NAME_SQL = "SELECT kt.token_id, kt.issue_time, kt.expiration, kt.max_lifetime, ktm.md_name, ktm.md_value FROM " + TOKENS_TABLE_NAME
       + " kt, " + TOKEN_METADATA_TABLE_NAME + " ktm WHERE kt.token_id = ktm.token_id AND kt.token_id IN (SELECT token_id FROM " + TOKEN_METADATA_TABLE_NAME + " WHERE md_name = '" + TokenMetadata.USER_NAME + "' AND md_value = ? )"
       + " ORDER BY kt.issue_time";
+  private static final String GET_TOKENS_CREATED_BY_USER_NAME_SQL = "SELECT kt.token_id, kt.issue_time, kt.expiration, kt.max_lifetime, ktm.md_name, ktm.md_value FROM " + TOKENS_TABLE_NAME
+      + " kt, " + TOKEN_METADATA_TABLE_NAME + " ktm WHERE kt.token_id = ktm.token_id AND kt.token_id IN (SELECT token_id FROM " + TOKEN_METADATA_TABLE_NAME + " WHERE md_name = '" + TokenMetadata.CREATED_BY + "' AND md_value = ? )"
+      + " ORDER BY kt.issue_time";
 
   private final DataSource dataSource;
 
@@ -203,11 +206,19 @@ public class TokenStateDatabase {
   }
 
   Collection<KnoxToken> getTokens(String userName) throws SQLException {
+    return fetchTokens(userName, GET_TOKENS_BY_USER_NAME_SQL);
+  }
+
+  Collection<KnoxToken> getDoAsTokens(String userName) throws SQLException {
+    return fetchTokens(userName, GET_TOKENS_CREATED_BY_USER_NAME_SQL);
+  }
+
+  private Collection<KnoxToken> fetchTokens(String userName, String sql) throws SQLException {
     Map<String, KnoxToken> tokenMap = new LinkedHashMap<>();
-    try (Connection connection = dataSource.getConnection(); PreparedStatement getTokenIdsStatement = connection.prepareStatement(GET_TOKENS_BY_USER_NAME_SQL)) {
+    try (Connection connection = dataSource.getConnection(); PreparedStatement getTokenIdsStatement = connection.prepareStatement(sql)) {
       getTokenIdsStatement.setString(1, userName);
       try (ResultSet rs = getTokenIdsStatement.executeQuery()) {
-        while(rs.next()) {
+        while (rs.next()) {
           String tokenId = rs.getString(1);
           long issueTime = rs.getLong(2);
           long expiration = rs.getLong(3);
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
index 956ff9a..f24c188 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
@@ -246,4 +246,7 @@ public interface TokenStateServiceMessages {
 
   @Message(level = MessageLevel.ERROR, text = "An error occurred while fetching tokens for user {0} from the database : {1}")
   void errorFetchingTokensForUserFromDatabase(String userName, String errorMessage, @StackTrace(level = MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR, text = "An error occurred while fetching impersonation tokens for user {0} from the database : {1}")
+  void errorFetchingDoAsTokensForUserFromDatabase(String userName, String errorMessage, @StackTrace(level = MessageLevel.DEBUG) Exception e);
 }
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
index 3845a07..2efe159 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
@@ -48,8 +48,7 @@ import static org.junit.Assert.assertTrue;
 public class DefaultTokenAuthorityServiceTest {
   @Test
   public void testTokenCreation() throws Exception {
-    Principal principal = EasyMock.createNiceMock(Principal.class);
-    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+    final String userName = "john.doe@example.com";
 
     GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
     String basedir = System.getProperty("basedir");
@@ -74,7 +73,7 @@ public class DefaultTokenAuthorityServiceTest {
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
 
-    EasyMock.replay(principal, config, ms, as);
+    EasyMock.replay(config, ms, as);
 
     DefaultKeystoreService ks = new DefaultKeystoreService();
     ks.setMasterService(ms);
@@ -88,7 +87,7 @@ public class DefaultTokenAuthorityServiceTest {
     ta.init(config, new HashMap<>());
     ta.start();
 
-    JWT token = ta.issueToken(new JWTokenAttributesBuilder().setPrincipal(principal).setAlgorithm("RS256").setManaged(true).build());
+    JWT token = ta.issueToken(new JWTokenAttributesBuilder().setUserName(userName).setAlgorithm("RS256").setManaged(true).build());
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
     assertTrue(Boolean.parseBoolean(token.getClaim(JWTToken.MANAGED_TOKEN_CLAIM)));
@@ -98,8 +97,7 @@ public class DefaultTokenAuthorityServiceTest {
 
   @Test
   public void testTokenCreationAudience() throws Exception {
-    Principal principal = EasyMock.createNiceMock(Principal.class);
-    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+    final String userName = "john.doe@example.com";
 
     GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
     String basedir = System.getProperty("basedir");
@@ -124,7 +122,7 @@ public class DefaultTokenAuthorityServiceTest {
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
 
-    EasyMock.replay(principal, config, ms, as);
+    EasyMock.replay(config, ms, as);
 
     DefaultKeystoreService ks = new DefaultKeystoreService();
     ks.setMasterService(ms);
@@ -139,7 +137,7 @@ public class DefaultTokenAuthorityServiceTest {
     ta.start();
 
     JWT token = ta
-        .issueToken(new JWTokenAttributesBuilder().setPrincipal(principal).setAudiences("https://login.example.com").setAlgorithm("RS256").build());
+        .issueToken(new JWTokenAttributesBuilder().setUserName(userName).setAudiences("https://login.example.com").setAlgorithm("RS256").build());
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
     assertEquals("https://login.example.com", token.getAudience());
@@ -149,8 +147,7 @@ public class DefaultTokenAuthorityServiceTest {
 
   @Test
   public void testTokenCreationNullAudience() throws Exception {
-    Principal principal = EasyMock.createNiceMock(Principal.class);
-    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+    final String userName = "john.doe@example.com";
 
     GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
     String basedir = System.getProperty("basedir");
@@ -175,7 +172,7 @@ public class DefaultTokenAuthorityServiceTest {
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
 
-    EasyMock.replay(principal, config, ms, as);
+    EasyMock.replay(config, ms, as);
 
     DefaultKeystoreService ks = new DefaultKeystoreService();
     ks.setMasterService(ms);
@@ -189,7 +186,7 @@ public class DefaultTokenAuthorityServiceTest {
     ta.init(config, new HashMap<>());
     ta.start();
 
-    JWT token = ta.issueToken(new JWTokenAttributesBuilder().setPrincipal(principal).setAlgorithm("RS256").build());
+    JWT token = ta.issueToken(new JWTokenAttributesBuilder().setUserName(userName).setAlgorithm("RS256").build());
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
 
@@ -198,8 +195,7 @@ public class DefaultTokenAuthorityServiceTest {
 
   @Test
   public void testTokenCreationSignatureAlgorithm() throws Exception {
-    Principal principal = EasyMock.createNiceMock(Principal.class);
-    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+    final String userName = "john.doe@example.com";
 
     GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
     String basedir = System.getProperty("basedir");
@@ -224,7 +220,7 @@ public class DefaultTokenAuthorityServiceTest {
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
 
-    EasyMock.replay(principal, config, ms, as);
+    EasyMock.replay(config, ms, as);
 
     DefaultKeystoreService ks = new DefaultKeystoreService();
     ks.setMasterService(ms);
@@ -238,7 +234,7 @@ public class DefaultTokenAuthorityServiceTest {
     ta.init(config, new HashMap<>());
     ta.start();
 
-    JWT token = ta.issueToken(new JWTokenAttributesBuilder().setPrincipal(principal).setAlgorithm("RS512").build());
+    JWT token = ta.issueToken(new JWTokenAttributesBuilder().setUserName(userName).setAlgorithm("RS512").build());
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
     assertTrue(token.getHeader().contains("RS512"));
@@ -248,8 +244,7 @@ public class DefaultTokenAuthorityServiceTest {
 
   @Test (expected = TokenServiceException.class)
   public void testTokenCreationBadSignatureAlgorithm() throws Exception {
-    Principal principal = EasyMock.createNiceMock(Principal.class);
-    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+    final String userName = "john.doe@example.com";
 
     GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
     String basedir = System.getProperty("basedir");
@@ -274,7 +269,7 @@ public class DefaultTokenAuthorityServiceTest {
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
 
-    EasyMock.replay(principal, config, ms, as);
+    EasyMock.replay(config, ms, as);
 
     DefaultKeystoreService ks = new DefaultKeystoreService();
     ks.setMasterService(ms);
@@ -286,7 +281,7 @@ public class DefaultTokenAuthorityServiceTest {
     ta.setKeystoreService(ks);
 
     ta.init(config, new HashMap<>());
-    ta.issueToken(new JWTokenAttributesBuilder().setPrincipal(principal).setAlgorithm("none").build());
+    ta.issueToken(new JWTokenAttributesBuilder().setUserName(userName).setAlgorithm("none").build());
   }
 
   @Test
@@ -303,8 +298,7 @@ public class DefaultTokenAuthorityServiceTest {
     String customSigningKeyAlias = "testSigningKeyAlias";
     String customSigningKeyPassphrase = "testSigningKeyPassphrase";
 
-    Principal principal = EasyMock.createNiceMock(Principal.class);
-    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+    final String userName = "john.doe@example.com";
 
     GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
     String basedir = System.getProperty("basedir");
@@ -328,7 +322,7 @@ public class DefaultTokenAuthorityServiceTest {
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
 
-    EasyMock.replay(principal, config, ms, as);
+    EasyMock.replay(config, ms, as);
 
     DefaultKeystoreService ks = new DefaultKeystoreService();
     ks.setMasterService(ms);
@@ -340,7 +334,7 @@ public class DefaultTokenAuthorityServiceTest {
     ta.init(config, new HashMap<>());
     ta.start();
 
-    final JWTokenAttributes jwtAttributes = new JWTokenAttributesBuilder().setPrincipal(principal).setAudiences(Collections.emptyList()).setAlgorithm("RS256").setExpires(-1)
+    final JWTokenAttributes jwtAttributes = new JWTokenAttributesBuilder().setUserName(userName).setAudiences(Collections.emptyList()).setAlgorithm("RS256").setExpires(-1)
         .setSigningKeystoreName(customSigningKeyName).setSigningKeystoreAlias(customSigningKeyAlias).setSigningKeystorePassphrase(customSigningKeyPassphrase.toCharArray()).build();
     JWT token = ta.issueToken(jwtAttributes);
     assertEquals("KNOXSSO", token.getIssuer());
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
index a902199..522e3b9 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
@@ -262,7 +262,7 @@ public class WebSSOResource {
         signingKeystorePassphrase = as.getPasswordFromAliasForCluster(clusterName, signingKeystorePassphraseAlias);
       }
 
-      final JWTokenAttributes jwtAttributes = new JWTokenAttributesBuilder().setPrincipal(p).setAudiences(targetAudiences).setAlgorithm(signatureAlgorithm).setExpires(getExpiry())
+      final JWTokenAttributes jwtAttributes = new JWTokenAttributesBuilder().setUserName(p.getName()).setAudiences(targetAudiences).setAlgorithm(signatureAlgorithm).setExpires(getExpiry())
           .setSigningKeystoreName(signingKeystoreName).setSigningKeystoreAlias(signingKeystoreAlias).setSigningKeystorePassphrase(signingKeystorePassphrase).build();
       JWT token = tokenAuthority.issueToken(jwtAttributes);
 
diff --git a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index a28a589..be85e76 100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -772,7 +772,7 @@ public class WebSSOResourceTest {
         throws TokenServiceException {
       String[] claimArray = new String[6];
       claimArray[0] = "KNOXSSO";
-      claimArray[1] = jwtAttributes.getPrincipal().getName();
+      claimArray[1] = jwtAttributes.getUserName();
       claimArray[2] = null;
       if (jwtAttributes.getExpires() == -1) {
         claimArray[3] = null;
diff --git a/gateway-service-knoxtoken/pom.xml b/gateway-service-knoxtoken/pom.xml
index cd0a884..30a762d 100644
--- a/gateway-service-knoxtoken/pom.xml
+++ b/gateway-service-knoxtoken/pom.xml
@@ -85,6 +85,22 @@
             <artifactId>json-smart</artifactId>
             <scope>compile</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.activation</groupId>
+                    <artifactId>javax.activation-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>dnsjava</groupId>
+                    <artifactId>dnsjava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-test-utils</artifactId>
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index ea03439..2d227cd 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@ -19,7 +19,6 @@ package org.apache.knox.gateway.service.knoxtoken;
 
 import java.nio.charset.StandardCharsets;
 import java.security.KeyStoreException;
-import java.security.Principal;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
@@ -58,6 +57,9 @@ import com.nimbusds.jose.crypto.MACSigner;
 import com.nimbusds.jose.util.ByteUtils;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.authorize.AuthorizationException;
+import org.apache.hadoop.security.authorize.ProxyUsers;
 import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.security.SubjectUtils;
@@ -80,6 +82,7 @@ import org.apache.knox.gateway.services.security.token.UnknownTokenException;
 import org.apache.knox.gateway.services.security.token.impl.JWT;
 import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 import org.apache.knox.gateway.services.security.token.impl.TokenMAC;
+import org.apache.knox.gateway.util.AuthFilterUtils;
 import org.apache.knox.gateway.util.JsonUtils;
 import org.apache.knox.gateway.util.Tokens;
 
@@ -132,6 +135,9 @@ public class TokenResource {
   static final String ENABLE_PATH = "/enable";
   static final String DISABLE_PATH = "/disable";
   private static final String TARGET_ENDPOINT_PULIC_CERT_PEM = "knox.token.target.endpoint.cert.pem";
+  static final String QUERY_PARAMETER_DOAS = "doAs";
+  static final String PROXYUSER_PREFIX = "knox.token.proxyuser";
+
   private static TokenServiceMessages log = MessagesFactory.get(TokenServiceMessages.class);
   private long tokenTTL = TOKEN_TTL_DEFAULT;
   private String tokenType;
@@ -289,6 +295,9 @@ public class TokenResource {
       }
     }
     setTokenStateServiceStatusMap();
+
+    final Configuration conf = AuthFilterUtils.getProxyUserConfiguration(context, PROXYUSER_PREFIX);
+    ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX);
   }
 
   private String getTokenTTLAsText() {
@@ -424,7 +433,8 @@ public class TokenResource {
       });
 
       final String userName = uriInfo.getQueryParameters().getFirst("userName");
-      final Collection<KnoxToken> userTokens = tokenStateService.getTokens(userName);
+      final String createdBy = uriInfo.getQueryParameters().getFirst("createdBy");
+      final Collection<KnoxToken> userTokens = createdBy == null ? tokenStateService.getTokens(userName) : tokenStateService.getDoAsTokens(createdBy);
       final Collection<KnoxToken> tokens = new TreeSet<>();
       if (metadataMap.isEmpty()) {
         tokens.addAll(userTokens);
@@ -672,7 +682,24 @@ public class TokenResource {
         .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 
     JWTokenAuthority ts = services.getService(ServiceType.TOKEN_SERVICE);
-    Principal p = request.getUserPrincipal();
+
+    String userName = request.getUserPrincipal().getName();
+    String createdBy = null;
+    // checking the doAs user only makes sense if tokens are managed (this is where we store the userName information)
+    if (tokenStateService != null) {
+      final String doAsUser = request.getParameter(QUERY_PARAMETER_DOAS);
+      if (doAsUser != null && !doAsUser.equals(userName)) {
+        try {
+          //this call will authorize the doAs request
+          AuthFilterUtils.authorizeImpersonationRequest(request, doAsUser);
+          createdBy = userName;
+          userName = doAsUser;
+        } catch (AuthorizationException e) {
+          return Response.status(Response.Status.FORBIDDEN).entity("{ \"" + e.getMessage() + "\" }").build();
+        }
+      }
+    }
+
     long expires = getExpiry();
 
     if (endpointPublicCert == null) {
@@ -699,15 +726,15 @@ public class TokenResource {
 
     if (tokenStateService != null) {
       if (tokenLimitPerUser != -1) { // if -1 => unlimited tokens for all users
-        final Collection<KnoxToken> userTokens = tokenStateService.getTokens(p.getName());
+        final Collection<KnoxToken> userTokens = tokenStateService.getTokens(userName);
         if (userTokens.size() >= tokenLimitPerUser) {
-          log.tokenLimitExceeded(p.getName());
+          log.tokenLimitExceeded(userName);
           if (UserLimitExceededAction.RETURN_ERROR == userLimitExceededAction) {
             return Response.status(Response.Status.FORBIDDEN).entity("{ \"Unable to get token - token limit exceeded.\" }").build();
           } else {
             // userTokens is an ordered collection (by issue time) -> the first element is the oldest one
             final String oldestTokenId = userTokens.iterator().next().getTokenId();
-            log.generalInfoMessage(String.format(Locale.getDefault(), "Revoking %s's oldest token %s ...", p.getName(), Tokens.getTokenIDDisplayText(oldestTokenId)));
+            log.generalInfoMessage(String.format(Locale.getDefault(), "Revoking %s's oldest token %s ...", userName, Tokens.getTokenIDDisplayText(oldestTokenId)));
             revoke(oldestTokenId);
            }
         }
@@ -720,7 +747,7 @@ public class TokenResource {
       JWTokenAttributes jwtAttributes;
       final JWTokenAttributesBuilder jwtAttributesBuilder = new JWTokenAttributesBuilder();
       jwtAttributesBuilder
-          .setPrincipal(p)
+          .setUserName(userName)
           .setAlgorithm(signatureAlgorithm)
           .setExpires(expires)
           .setManaged(managedToken)
@@ -766,9 +793,12 @@ public class TokenResource {
                                      expires,
                                      maxTokenLifetime.orElse(tokenStateService.getDefaultMaxLifetimeDuration()));
           final String comment = request.getParameter(COMMENT);
-          final TokenMetadata tokenMetadata = new TokenMetadata(p.getName(), StringUtils.isBlank(comment) ? null : comment);
-          tokenMetadata.setPasscode(tokenMAC.hash(tokenId, issueTime, p.getName(), passcode));
+          final TokenMetadata tokenMetadata = new TokenMetadata(userName, StringUtils.isBlank(comment) ? null : comment);
+          tokenMetadata.setPasscode(tokenMAC.hash(tokenId, issueTime, userName, passcode));
           addArbitraryTokenMetadata(tokenMetadata);
+          if (createdBy != null) {
+            tokenMetadata.setCreatedBy(createdBy);
+          }
           tokenStateService.addMetadata(tokenId, tokenMetadata);
           log.storedToken(getTopologyName(), Tokens.getTokenDisplayText(accessToken), Tokens.getTokenIDDisplayText(tokenId));
         }
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index eaf514f..a443106 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -86,6 +86,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -97,6 +98,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.function.Predicate;
 
 /**
  * Some tests for the token service
@@ -147,6 +149,7 @@ public class TokenServiceResourceTest {
   private void configureCommonExpectations(Map<String, String> contextExpectations, String expectedSubjectDN, Boolean serverManagedTssEnabled) throws Exception {
     context = EasyMock.createNiceMock(ServletContext.class);
     contextExpectations.forEach((key, value) -> EasyMock.expect(context.getInitParameter(key)).andReturn(value).anyTimes());
+    EasyMock.expect(context.getInitParameterNames()).andReturn(Collections.enumeration(contextExpectations.keySet())).anyTimes();
     request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -156,6 +159,9 @@ public class TokenServiceResourceTest {
     if (contextExpectations.containsKey(TokenResource.LIFESPAN)) {
       EasyMock.expect(request.getParameter(TokenResource.LIFESPAN)).andReturn(contextExpectations.get(TokenResource.LIFESPAN)).anyTimes();
     }
+    if (contextExpectations.containsKey(TokenResource.QUERY_PARAMETER_DOAS)) {
+      EasyMock.expect(request.getParameter(TokenResource.QUERY_PARAMETER_DOAS)).andReturn(contextExpectations.get(TokenResource.QUERY_PARAMETER_DOAS)).anyTimes();
+    }
     EasyMock.expect(request.getParameterNames()).andReturn(Collections.emptyEnumeration()).anyTimes();
 
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
@@ -1026,8 +1032,12 @@ public class TokenServiceResourceTest {
   }
 
   private Response getUserTokensResponse(TokenResource tokenResource) {
+    return getUserTokensResponse(tokenResource, false);
+  }
+
+  private Response getUserTokensResponse(TokenResource tokenResource, boolean createdBy) {
     final MultivaluedMap<String, String> queryParameters = new MultivaluedHashMap<>();
-    queryParameters.put("userName", Arrays.asList(USER_NAME));
+    queryParameters.put(createdBy ? "createdBy" : "userName", Arrays.asList(USER_NAME));
     final UriInfo uriInfo = EasyMock.createNiceMock(UriInfo.class);
     EasyMock.expect(uriInfo.getQueryParameters()).andReturn(queryParameters).anyTimes();
     EasyMock.replay(uriInfo);
@@ -1082,6 +1092,31 @@ public class TokenServiceResourceTest {
     assertEquals(tokens.size(), revokeOldestToken ? configuredLimit : numberOfTokens);
   }
 
+  @Test
+  public void testCreateImpersonatedToken() throws Exception {
+    final String impersonatedUser = "testUser";
+    final Map<String, String> contextExpectations = new HashMap<>();
+    contextExpectations.put(TokenResource.QUERY_PARAMETER_DOAS, impersonatedUser);
+    contextExpectations.put(TokenResource.PROXYUSER_PREFIX + "." + USER_NAME + ".users", impersonatedUser);
+    contextExpectations.put(TokenResource.PROXYUSER_PREFIX + "." + USER_NAME + ".hosts", "*");
+    configureCommonExpectations(contextExpectations, Boolean.TRUE);
+
+    final TokenResource tr = new TokenResource();
+    tr.request = request;
+    tr.context = context;
+    tr.init();
+
+    tr.doGet();
+
+    final Response getKnoxTokensResponse = getUserTokensResponse(tr, true);
+    final Collection<LinkedHashMap<String, Object>> tokens = ((Map<String, Collection<LinkedHashMap<String, Object>>>) JsonUtils
+        .getObjectFromJsonString(getKnoxTokensResponse.getEntity().toString())).get("tokens");
+    final LinkedHashMap<String, Object> knoxToken = tokens.iterator().next();
+    final Map<String, String> metadata = (Map<String, String>) knoxToken.get("metadata");
+    assertEquals(metadata.get("createdBy"), USER_NAME);
+    assertEquals(metadata.get("userName"), impersonatedUser);
+  }
+
   /**
    *
    * @param isTokenStateServerManaged true, if server-side token state management should be enabled; Otherwise, false or null.
@@ -1478,11 +1513,26 @@ public class TokenServiceResourceTest {
 
     @Override
     public Collection<KnoxToken> getTokens(String userName) {
+      return fetchTokens(userName, false);
+    }
+
+    @Override
+    public Collection<KnoxToken> getDoAsTokens(String createdBy) {
+      return fetchTokens(createdBy, true);
+    }
+
+    private Collection<KnoxToken> fetchTokens(String userName, boolean createdBy) {
       final Collection<KnoxToken> tokens = new TreeSet<>();
-      tokenMetadata.entrySet().stream().filter(entry -> entry.getValue().getUserName().equals(userName)).forEach(metadata -> {
+      final Predicate<Map.Entry<String, TokenMetadata>> filterPredicate;
+      if (createdBy) {
+        filterPredicate = entry -> userName.equals(entry.getValue().getCreatedBy());
+      } else {
+        filterPredicate = entry -> userName.equals(entry.getValue().getUserName());
+      }
+      tokenMetadata.entrySet().stream().filter(filterPredicate).forEach(metadata -> {
         String tokenId = metadata.getKey();
         try {
-          tokens.add(new KnoxToken(tokenId, getTokenIssueTime(tokenId), getTokenExpiration(tokenId), 0L, metadata.getValue()));
+          tokens.add(new KnoxToken(tokenId, getTokenIssueTime(tokenId), getTokenExpiration(tokenId), getMaxLifetime(tokenId), metadata.getValue()));
         } catch (UnknownTokenException e) {
           // NOP
         }
@@ -1523,7 +1573,7 @@ public class TokenServiceResourceTest {
     public JWT issueToken(JWTokenAttributes jwtAttributes) {
       String[] claimArray = new String[6];
       claimArray[0] = "KNOXSSO";
-      claimArray[1] = jwtAttributes.getPrincipal().getName();
+      claimArray[1] = jwtAttributes.getUserName();
       claimArray[2] = null;
       if (jwtAttributes.getExpires() == -1) {
         claimArray[3] = null;
diff --git a/gateway-spi/pom.xml b/gateway-spi/pom.xml
index 63129b1..95b07c1 100644
--- a/gateway-spi/pom.xml
+++ b/gateway-spi/pom.xml
@@ -169,6 +169,21 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.activation</groupId>
+                    <artifactId>javax.activation-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>dnsjava</groupId>
+                    <artifactId>dnsjava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-test-utils</artifactId>
             <scope>test</scope>
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributes.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributes.java
index 6f3a9fa..b7e7a82 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributes.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributes.java
@@ -17,12 +17,11 @@
  */
 package org.apache.knox.gateway.services.security.token;
 
-import java.security.Principal;
 import java.util.List;
 
 public class JWTokenAttributes {
 
-  private final Principal principal;
+  private final String userName;
   private final List<String> audiences;
   private final String algorithm;
   private final long expires;
@@ -33,10 +32,9 @@ public class JWTokenAttributes {
   private final String jku;
   private final String type;
 
-  JWTokenAttributes(Principal principal, List<String> audiences, String algorithm, long expires, String signingKeystoreName, String signingKeystoreAlias,
+  JWTokenAttributes(String userName, List<String> audiences, String algorithm, long expires, String signingKeystoreName, String signingKeystoreAlias,
       char[] signingKeystorePassphrase, boolean managed, String jku, String type) {
-    super();
-    this.principal = principal;
+    this.userName = userName;
     this.audiences = audiences;
     this.algorithm = algorithm;
     this.expires = expires;
@@ -48,8 +46,8 @@ public class JWTokenAttributes {
     this.type = type;
   }
 
-  public Principal getPrincipal() {
-    return principal;
+  public String getUserName() {
+    return userName;
   }
 
   public List<String> getAudiences() {
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributesBuilder.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributesBuilder.java
index feb0758..faf4bf3 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributesBuilder.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAttributesBuilder.java
@@ -17,15 +17,12 @@
  */
 package org.apache.knox.gateway.services.security.token;
 
-import java.security.Principal;
 import java.util.Collections;
 import java.util.List;
 
-import javax.security.auth.Subject;
-
 public class JWTokenAttributesBuilder {
 
-  private Principal principal;
+  private String userName;
   private List<String> audiences;
   private String algorithm;
   private long expires;
@@ -36,12 +33,8 @@ public class JWTokenAttributesBuilder {
   private String jku;
   private String type;
 
-  public JWTokenAttributesBuilder setPrincipal(Subject subject) {
-    return setPrincipal((Principal) subject.getPrincipals().toArray()[0]);
-  }
-
-  public JWTokenAttributesBuilder setPrincipal(Principal principal) {
-    this.principal = principal;
+  public JWTokenAttributesBuilder setUserName(String userName) {
+    this.userName = userName;
     return this;
   }
 
@@ -95,7 +88,7 @@ public class JWTokenAttributesBuilder {
   }
 
   public JWTokenAttributes build() {
-    return new JWTokenAttributes(principal, (audiences == null ? Collections.emptyList() : audiences), algorithm, expires, signingKeystoreName, signingKeystoreAlias,
+    return new JWTokenAttributes(userName, (audiences == null ? Collections.emptyList() : audiences), algorithm, expires, signingKeystoreName, signingKeystoreAlias,
         signingKeystorePassphrase, managed, jku, type);
   }
 
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
index 8fa49cd..b90dfb4 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
@@ -35,7 +35,8 @@ public class TokenMetadata {
   public static final String COMMENT = "comment";
   public static final String ENABLED = "enabled";
   public static final String PASSCODE = "passcode";
-  private static final List<String> KNOWN_MD_NAMES = Arrays.asList(USER_NAME, COMMENT, ENABLED, PASSCODE);
+  public static final String CREATED_BY = "createdBy";
+  private static final List<String> KNOWN_MD_NAMES = Arrays.asList(USER_NAME, COMMENT, ENABLED, PASSCODE, CREATED_BY);
 
   private final Map<String, String> metadataMap = new HashMap<>();
 
@@ -85,11 +86,11 @@ public class TokenMetadata {
   }
 
   public String getUserName() {
-    return metadataMap.get(USER_NAME);
+    return getMetadata(USER_NAME);
   }
 
   public String getComment() {
-    return metadataMap.get(COMMENT);
+    return getMetadata(COMMENT);
   }
 
   public void setEnabled(boolean enabled) {
@@ -97,7 +98,7 @@ public class TokenMetadata {
   }
 
   public boolean isEnabled() {
-    return Boolean.parseBoolean(metadataMap.get(ENABLED));
+    return Boolean.parseBoolean(getMetadata(ENABLED));
   }
 
   public void setPasscode(String passcode) {
@@ -106,7 +107,15 @@ public class TokenMetadata {
 
   @JsonIgnore
   public String getPasscode() {
-    return metadataMap.get(PASSCODE);
+    return getMetadata(PASSCODE);
+  }
+
+  public void setCreatedBy(String createdBy) {
+    saveMetadata(CREATED_BY, createdBy);
+  }
+
+  public String getCreatedBy() {
+    return getMetadata(CREATED_BY);
   }
 
   public String toJSON() {
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java
index 72ad874..56df224 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java
@@ -203,4 +203,13 @@ public interface TokenStateService extends Service {
    */
   Collection<KnoxToken> getTokens(String userName);
 
+  /**
+   * @param createdBy the user name that identified the CREATED_BY metadata (the
+   *                  person who created the token for the token's user as 'doAs'
+   * @return a collection of tokens associated to the given 'created by' user;
+   *         it's an empty collection if there is no associated token found in the
+   *         underlying token management backend
+   */
+  Collection<KnoxToken> getDoAsTokens(String createdBy);
+
 }
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java b/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
index e58c8f7..aeea228 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
@@ -18,9 +18,19 @@
 package org.apache.knox.gateway.util;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AuthorizationException;
+import org.apache.hadoop.security.authorize.ProxyUsers;
 
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import java.security.Principal;
+import java.util.Enumeration;
 import java.util.Set;
 import java.util.StringTokenizer;
 
@@ -67,4 +77,76 @@ public class AuthFilterUtils {
       AuthFilterUtils.parseStringThenAdd(unAuthenticatedPaths, list);
     }
   }
+
+  public static Configuration getProxyUserConfiguration(ServletContext context, String prefix) {
+    if (context == null) {
+      throw new IllegalArgumentException("Cannot get proxyuser configuration from NULL context");
+    }
+    return getProxyUserConfiguration(context, null, prefix);
+  }
+
+  public static Configuration getProxyUserConfiguration(FilterConfig filterConfig, String prefix) {
+    if (filterConfig == null) {
+      throw new IllegalArgumentException("Cannot get proxyuser configuration from NULL filter config");
+    }
+    return getProxyUserConfiguration(null, filterConfig, prefix);
+  }
+
+  private static Configuration getProxyUserConfiguration(ServletContext context, FilterConfig filterConfig, String prefix) {
+    final Configuration conf = new Configuration(false);
+    final Enumeration<?> names = context == null ? filterConfig.getInitParameterNames() : context.getInitParameterNames();
+    if (names != null) {
+      while (names.hasMoreElements()) {
+        String name = (String) names.nextElement();
+        if (name.startsWith(prefix + ".")) {
+          String value = context == null ? filterConfig.getInitParameter(name) : context.getInitParameter(name);
+          conf.set(name, value);
+        }
+      }
+    }
+
+    return conf;
+  }
+
+  public static HttpServletRequest getProxyRequest(HttpServletRequest request, String doAsUser) throws AuthorizationException {
+    final UserGroupInformation remoteRequestUgi = getRemoteRequestUgi(request, doAsUser);
+    if (remoteRequestUgi != null) {
+      authorizeImpersonationRequest(request, remoteRequestUgi);
+
+      return new HttpServletRequestWrapper(request) {
+        @Override
+        public String getRemoteUser() {
+          return remoteRequestUgi.getShortUserName();
+        }
+
+        @Override
+        public Principal getUserPrincipal() {
+          return remoteRequestUgi::getUserName;
+        }
+      };
+
+    }
+    return null;
+  }
+
+  public static void authorizeImpersonationRequest(HttpServletRequest request, String doAsUser) throws AuthorizationException {
+    final UserGroupInformation remoteRequestUgi = getRemoteRequestUgi(request, doAsUser);
+    if (remoteRequestUgi != null) {
+      authorizeImpersonationRequest(request, remoteRequestUgi);
+    }
+  }
+
+  private static void authorizeImpersonationRequest(HttpServletRequest request, UserGroupInformation remoteRequestUgi) throws AuthorizationException {
+    ProxyUsers.authorize(remoteRequestUgi, request.getRemoteAddr());
+  }
+
+  private static UserGroupInformation getRemoteRequestUgi(HttpServletRequest request, String doAsUser) {
+    if (request.getUserPrincipal() != null) {
+      final String remoteUser = request.getUserPrincipal().getName();
+      final UserGroupInformation remoteUserUgi = UserGroupInformation.createRemoteUser(remoteUser);
+      return UserGroupInformation.createProxyUser(doAsUser, remoteUserUgi);
+    }
+    return null;
+  }
+
 }
diff --git a/knox-token-management-ui/token-management/app/token.management.component.html b/knox-token-management-ui/token-management/app/token.management.component.html
index 4e5c6fd..7aebe25 100644
--- a/knox-token-management-ui/token-management/app/token.management.component.html
+++ b/knox-token-management-ui/token-management/app/token.management.component.html
@@ -16,12 +16,13 @@
 
     <div>
         <button (click)="gotoTokenGenerationPage();">Generate New Token</button>
-        <button type="button" title="Refresh Knox Tokens" (click)="fetchKnoxTokens();">
+        <button type="button" title="Refresh Knox Tokens" (click)="fetchAllKnoxTokens();">
             <span class="glyphicon glyphicon-refresh"></span>
         </button>
     </div>
-    <div class="table-responsive" style="width:100%; overflow: auto; overflow-y: scroll">
-        <table class="table table-hover" [mfData]="knoxTokens" #mf="mfDataTable" [mfRowsOnPage]="10">
+    <div class="table-responsive" style="width:100%; overflow: auto; overflow-y: scroll; padding: 10px 0px 0px 0px;">
+        <label>My Knox Tokens</label>
+        <table class="table table-hover" [mfData]="knoxTokens" #tokens="mfDataTable" [mfRowsOnPage]="10">
             <thead>
             <tr>
                 <th>Token ID</th>
@@ -33,7 +34,7 @@
             </tr>
             </thead>
             <tbody>
-            <tr *ngFor="let knoxToken of mf.data">
+            <tr *ngFor="let knoxToken of tokens.data">
                 <td>{{knoxToken.tokenId}}</td>
                 <td>{{formatDateTime(knoxToken.issueTimeLong)}}</td>
                 <td *ngIf="!isTokenExpired(knoxToken.expirationLong)" style="color: green">{{formatDateTime(knoxToken.expirationLong)}}</td>
@@ -62,5 +63,47 @@
 		    </tfoot>
         </table>
     </div>
+
+    <!-- 'doAs' Knox Tokens (tokens created by the current user on behalf on another user -->
+
+    <div class="table-responsive" style="width:100%; overflow: auto; overflow-y: scroll; padding: 10px 0px 0px 0px;">
+        <label>Impersonation Knox Tokens</label>
+        <table class="table table-hover" [mfData]="doAsKnoxTokens" #doAsTokens="mfDataTable" [mfRowsOnPage]="10">
+            <thead>
+            <tr>
+                <th>Token ID</th>
+                <th>Issued</th>
+                <th>Expires</th>
+                <th>Comment</th>
+                <th>Additional Metadata</th>
+                <th>Impersonated User<th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr *ngFor="let doAsKnoxtoken of doAsTokens.data">
+                <td>{{doAsKnoxtoken.tokenId}}</td>
+                <td>{{formatDateTime(doAsKnoxtoken.issueTimeLong)}}</td>
+                <td *ngIf="!isTokenExpired(doAsKnoxtoken.expirationLong)" style="color: green">{{formatDateTime(doAsKnoxtoken.expirationLong)}}</td>
+                <td *ngIf="isTokenExpired(doAsKnoxtoken.expirationLong)" style="color: red">{{formatDateTime(doAsKnoxtoken.expirationLong)}}</td>
+                <td>{{doAsKnoxtoken.metadata.comment}}</td>
+                <td>
+                  <ul>
+                    <li *ngFor="let metadata of getCustomMetadataArray(doAsKnoxtoken)">
+                      {{metadata[0]}} = {{metadata[1]}}
+                    </li>
+                  </ul>
+                </td>
+                <td>{{doAsKnoxtoken.metadata.userName}}</td>
+            </tr>
+            </tbody>
+		    <tfoot>
+		    <tr>
+		        <td colspan="6">
+		            <mfBootstrapPaginator [rowsOnPageSet]="[5,10,15]"></mfBootstrapPaginator>
+		        </td>
+		    </tr>
+		    </tfoot>
+        </table>
+    </div>
 </div>
 
diff --git a/knox-token-management-ui/token-management/app/token.management.component.ts b/knox-token-management-ui/token-management/app/token.management.component.ts
index 15a0e8c..e4ebc0d 100644
--- a/knox-token-management-ui/token-management/app/token.management.component.ts
+++ b/knox-token-management-ui/token-management/app/token.management.component.ts
@@ -30,6 +30,7 @@ export class TokenManagementComponent implements OnInit {
 
     userName: string;
     knoxTokens: KnoxToken[];
+    doAsKnoxTokens: KnoxToken[];
 
     toggleBoolean(propertyName: string) {
         this[propertyName] = !this[propertyName];
@@ -49,23 +50,29 @@ export class TokenManagementComponent implements OnInit {
 
     setUserName(userName: string) {
         this.userName = userName;
-        this.fetchKnoxTokens();
+        this.fetchAllKnoxTokens();
     }
 
-    fetchKnoxTokens(): void {
-        this.tokenManagementService.getKnoxTokens(this.userName).then(tokens => this.knoxTokens = tokens);
+    fetchAllKnoxTokens(): void {
+        this.fetchKnoxTokens(true);
+        this.fetchKnoxTokens(false);
+    }
+
+    fetchKnoxTokens(impersonated: boolean): void {
+        this.tokenManagementService.getKnoxTokens(this.userName, impersonated)
+            .then(tokens => impersonated ? this.doAsKnoxTokens = tokens : this.knoxTokens = tokens);
     }
 
     disableToken(tokenId: string) {
-        this.tokenManagementService.setEnabledDisabledFlag(false, tokenId).then((response: string) => this.fetchKnoxTokens());
+        this.tokenManagementService.setEnabledDisabledFlag(false, tokenId).then((response: string) => this.fetchAllKnoxTokens());
     }
 
     enableToken(tokenId: string) {
-        this.tokenManagementService.setEnabledDisabledFlag(true, tokenId).then((response: string) => this.fetchKnoxTokens());
+        this.tokenManagementService.setEnabledDisabledFlag(true, tokenId).then((response: string) => this.fetchAllKnoxTokens());
     }
 
     revokeToken(tokenId: string) {
-        this.tokenManagementService.revokeToken(tokenId).then((response: string) => this.fetchKnoxTokens());
+        this.tokenManagementService.revokeToken(tokenId).then((response: string) => this.fetchAllKnoxTokens());
     }
 
     gotoTokenGenerationPage() {
diff --git a/knox-token-management-ui/token-management/app/token.management.service.ts b/knox-token-management-ui/token-management/app/token.management.service.ts
index 7fa1ab6..4da100d 100644
--- a/knox-token-management-ui/token-management/app/token.management.service.ts
+++ b/knox-token-management-ui/token-management/app/token.management.service.ts
@@ -27,16 +27,18 @@ export class TokenManagementService {
     sessionUrl = window.location.pathname.replace(new RegExp('token-management/.*'), 'session/api/v1/sessioninfo');
     apiUrl = window.location.pathname.replace(new RegExp('token-management/.*'), 'knoxtoken/api/v1/token/');
     getKnoxTokensUrl = this.apiUrl + 'getUserTokens?userName=';
+    getDoAsKnoxTokensUrl = this.apiUrl + 'getUserTokens?createdBy=';
     enableKnoxTokenUrl = this.apiUrl + 'enable';
     disableKnoxTokenUrl = this.apiUrl + 'disable';
     revokeKnoxTokenUrl = this.apiUrl + 'revoke';
 
     constructor(private http: HttpClient) {}
 
-    getKnoxTokens(userName: string): Promise<KnoxToken[]> {
+    getKnoxTokens(userName: string, impersonated: boolean): Promise<KnoxToken[]> {
         let headers = new HttpHeaders();
         headers = this.addJsonHeaders(headers);
-        return this.http.get(this.getKnoxTokensUrl + userName, { headers: headers})
+        let urlToUse = impersonated ? this.getDoAsKnoxTokensUrl : this.getKnoxTokensUrl;
+        return this.http.get(urlToUse + userName, { headers: headers})
             .toPromise()
             .then(response => response['tokens'] as KnoxToken[])
             .catch((err: HttpErrorResponse) => {