[ 
https://issues.apache.org/jira/browse/KNOX-2839?focusedWorklogId=829873&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-829873
 ]

ASF GitHub Bot logged work on KNOX-2839:
----------------------------------------

                Author: ASF GitHub Bot
            Created on: 29/Nov/22 20:10
            Start Date: 29/Nov/22 20:10
    Worklog Time Spent: 10m 
      Work Description: lmccay commented on code in PR #681:
URL: https://github.com/apache/knox/pull/681#discussion_r1035199113


##########
gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java:
##########
@@ -187,21 +219,46 @@ public void doFilter(ServletRequest request, 
ServletResponse response, FilterCha
     }
 
     String principalName = getPrincipalName(subject);
+    String mappedPrincipalName = null;
+    try {
+      mappedPrincipalName = handleProxyUserImpersonation(request, 
principalName);
+    } catch(AuthorizationException e) {
+      LOG.hadoopAuthProxyUserFailed(e);
+      HttpExceptionUtils.createServletExceptionResponse((HttpServletResponse) 
response, HttpServletResponse.SC_FORBIDDEN, e);
+      return;
+    }
 
-    String mappedPrincipalName = mapUserPrincipalBase(principalName);
+    // mapping principal name using user principal mapping (if configured)
+    mappedPrincipalName = mapUserPrincipalBase(mappedPrincipalName);
     mappedPrincipalName = mapUserPrincipal(mappedPrincipalName);
+
     String[] mappedGroups = mapGroupPrincipalsBase(mappedPrincipalName, 
subject);
     String[] groups = mapGroupPrincipals(mappedPrincipalName, subject);
     String[] virtualGroups = virtualGroupMapper.mapGroups(mappedPrincipalName, 
combine(subject, groups), request).toArray(new String[0]);
     groups = combineGroupMappings(mappedGroups, groups);
     groups = combineGroupMappings(virtualGroups, groups);
 
-    HttpServletRequestWrapper wrapper = wrapHttpServletRequest(
-        request, mappedPrincipalName);
+    HttpServletRequestWrapper wrapper = wrapHttpServletRequest(request, 
mappedPrincipalName);
+
 
     continueChainAsPrincipal(wrapper, response, chain, mappedPrincipalName, 
unique(groups));
   }
 
+  private String handleProxyUserImpersonation(ServletRequest request, String 
principalName) throws AuthorizationException {
+    if (impersonationEnabled) {
+      final String doAsUser = 
request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS);
+      if (doAsUser != null && !doAsUser.equals(principalName)) {
+        LOG.hadoopAuthDoAsUser(doAsUser, principalName, 
request.getRemoteAddr());
+        if (principalName != null) {
+          AuthFilterUtils.authorizeImpersonationRequest((HttpServletRequest) 
request, principalName, doAsUser, topologyName, ROLE);
+          LOG.hadoopAuthProxyUserSuccess();

Review Comment:
   I think we have audit log details for mapped principals. If so, I think we 
need to add impersonation details as well.



##########
gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java:
##########
@@ -720,19 +705,21 @@ private Response getAuthenticationToken() {
     String createdBy = null;
     // checking the doAs user only makes sense if tokens are managed (this is 
where we store the userName information)
     // and if impersonation is enabled
-    if (impersonationEnabled && 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, 
getTopologyName(), TokenServiceDeploymentContributor.ROLE);
-          createdBy = userName;
-          userName = doAsUser;
-          log.tokenImpersonationSuccess(createdBy, doAsUser);
-        } catch (AuthorizationException e) {
-          log.tokenImpersonationFailed(e);
-          return Response.status(Response.Status.FORBIDDEN).entity("{ \"" + 
e.getMessage() + "\" }").build();
+    if (tokenStateService != null) {
+      final String realUserName = (String) 
request.getAttribute(AuthFilterUtils.REAL_USER_NAME_ATTRIBUTE);
+      final Subject subject = SubjectUtils.getCurrentSubject();
+      if (subject != null && SubjectUtils.isImpersonating(subject)) {
+        String primaryPrincipalName = 
SubjectUtils.getPrimaryPrincipalName(subject);
+        String impersonatedPrincipalName = 
SubjectUtils.getImpersonatedPrincipalName(subject);
+        if (!primaryPrincipalName.equals(impersonatedPrincipalName)) {
+          createdBy = primaryPrincipalName;
+          userName = impersonatedPrincipalName;
+          log.tokenImpersonationSuccess(createdBy, userName);

Review Comment:
   I could use some walkthrough of the logic here. If it is not more clear in 
the full context of the file then it may help to describe here in comments or 
make the code more obvious. I do think that I have it but would like to make 
sure.
   
   It seems that we are setting the createdBy and userName for the token 
metadata when there is indeed a stateService in play. If impersonation/doAs is 
enabled then we use the primaryPrincipal from the Subject to reflect the 
createdBy. If it isn't then we use the realUserName from the request 
attributes. I DO NOT KNOW HOW THIS IS SET OR WHATUSER IT REPRESENTS. If  
realUser sounds like hadoop auth module and UGI terms. Does this assume that 
the HadoopAuthProvider and/or the HadoopGroupProvider is being used? Given that 
the doAs support is being added to a base identity assertion provider class, is 
this a safe assumption? If so, is that because we have added a dependency on 
hadoop for all identity assertion providers somewhere along the line? Anyway, 
back to the logic flow...  In the case where subject is impersonating, we take 
the impersonatedPrincipalName as the userName. However, in the case that there 
is no impersonating, the userName is not set? Is this defaulted to the same as 
the createdBy somwhere else?



##########
gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java:
##########
@@ -187,21 +219,46 @@ public void doFilter(ServletRequest request, 
ServletResponse response, FilterCha
     }
 
     String principalName = getPrincipalName(subject);
+    String mappedPrincipalName = null;
+    try {
+      mappedPrincipalName = handleProxyUserImpersonation(request, 
principalName);
+    } catch(AuthorizationException e) {
+      LOG.hadoopAuthProxyUserFailed(e);
+      HttpExceptionUtils.createServletExceptionResponse((HttpServletResponse) 
response, HttpServletResponse.SC_FORBIDDEN, e);
+      return;
+    }
 
-    String mappedPrincipalName = mapUserPrincipalBase(principalName);
+    // mapping principal name using user principal mapping (if configured)
+    mappedPrincipalName = mapUserPrincipalBase(mappedPrincipalName);
     mappedPrincipalName = mapUserPrincipal(mappedPrincipalName);
+
     String[] mappedGroups = mapGroupPrincipalsBase(mappedPrincipalName, 
subject);
     String[] groups = mapGroupPrincipals(mappedPrincipalName, subject);
     String[] virtualGroups = virtualGroupMapper.mapGroups(mappedPrincipalName, 
combine(subject, groups), request).toArray(new String[0]);
     groups = combineGroupMappings(mappedGroups, groups);
     groups = combineGroupMappings(virtualGroups, groups);
 
-    HttpServletRequestWrapper wrapper = wrapHttpServletRequest(
-        request, mappedPrincipalName);
+    HttpServletRequestWrapper wrapper = wrapHttpServletRequest(request, 
mappedPrincipalName);
+
 
     continueChainAsPrincipal(wrapper, response, chain, mappedPrincipalName, 
unique(groups));
   }
 
+  private String handleProxyUserImpersonation(ServletRequest request, String 
principalName) throws AuthorizationException {
+    if (impersonationEnabled) {
+      final String doAsUser = 
request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS);
+      if (doAsUser != null && !doAsUser.equals(principalName)) {
+        LOG.hadoopAuthDoAsUser(doAsUser, principalName, 
request.getRemoteAddr());
+        if (principalName != null) {
+          AuthFilterUtils.authorizeImpersonationRequest((HttpServletRequest) 
request, principalName, doAsUser, topologyName, ROLE);
+          LOG.hadoopAuthProxyUserSuccess();
+          return doAsUser;

Review Comment:
   nit; Wouldn't principalName = doAsUser; be better here than returning a 
separate variable and potentially having to accommodate this later if there is 
code needed before the final return?



##########
gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java:
##########
@@ -720,19 +705,21 @@ private Response getAuthenticationToken() {
     String createdBy = null;
     // checking the doAs user only makes sense if tokens are managed (this is 
where we store the userName information)
     // and if impersonation is enabled
-    if (impersonationEnabled && 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, 
getTopologyName(), TokenServiceDeploymentContributor.ROLE);
-          createdBy = userName;
-          userName = doAsUser;
-          log.tokenImpersonationSuccess(createdBy, doAsUser);
-        } catch (AuthorizationException e) {
-          log.tokenImpersonationFailed(e);
-          return Response.status(Response.Status.FORBIDDEN).entity("{ \"" + 
e.getMessage() + "\" }").build();
+    if (tokenStateService != null) {
+      final String realUserName = (String) 
request.getAttribute(AuthFilterUtils.REAL_USER_NAME_ATTRIBUTE);
+      final Subject subject = SubjectUtils.getCurrentSubject();
+      if (subject != null && SubjectUtils.isImpersonating(subject)) {
+        String primaryPrincipalName = 
SubjectUtils.getPrimaryPrincipalName(subject);
+        String impersonatedPrincipalName = 
SubjectUtils.getImpersonatedPrincipalName(subject);
+        if (!primaryPrincipalName.equals(impersonatedPrincipalName)) {
+          createdBy = primaryPrincipalName;
+          userName = impersonatedPrincipalName;
+          log.tokenImpersonationSuccess(createdBy, userName);
         }
+      } else if (StringUtils.isNotBlank(realUserName) && 
!realUserName.equals(userName)) {
+        // real user name is set by HadoopAuth filter for impersonated 
requests (part of 'doAs' processing)
+        createdBy = realUserName;

Review Comment:
   The above comment does point to an assumption about the hadoopauth provider. 
Since we are adding this capability to the common identity assertion 
capabilities this is no longer true and it is not clear that the hadoop auth or 
hadoop groups providers are being used. I think that we need to make the above 
two conditional blocks more uniform and decoupled from hadoop 
terms/dependencies. In otherwords, by the time we get here we should only be 
relying on Subject Principals.





Issue Time Tracking
-------------------

    Worklog Id:     (was: 829873)
    Time Spent: 1h 50m  (was: 1h 40m)

> Refactor impersonation from KnoxToken service
> ---------------------------------------------
>
>                 Key: KNOX-2839
>                 URL: https://issues.apache.org/jira/browse/KNOX-2839
>             Project: Apache Knox
>          Issue Type: Task
>          Components: Server
>            Reporter: Sandor Molnar
>            Assignee: Sandor Molnar
>            Priority: Blocker
>             Fix For: 2.0.0
>
>          Time Spent: 1h 50m
>  Remaining Estimate: 0h
>
> With KNOX-2714, end-users can create tokens on behalf of other users using 
> Hadoop's impersonation mechanism.
> The problem with the current implementation is that the proxyuser 
> authorization happens to be on service level, but it should be executed 
> sooner.
> As discussed offline with [~lmccay] and [~pzampino] we agreed on the 
> following:
>  * impersonation support should be done in Knox's identity assertion layer 
> and not in the services
>  * the proxuyser authorization in HadoopAuth filter should be left as-is. 
> When someone configures them in two places (HadoopAuth authentication and in 
> identity-assertion), a WARN-level message should indicate that one on the 
> identity-assertion level will be ignored.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to