Explorar o código

fix issue #660 - Shortcut module does not display shortcuts based on LDAP query filter

Jason Rivard %!s(int64=3) %!d(string=hai) anos
pai
achega
f252c6de7b

+ 107 - 85
server/src/main/java/password/pwm/http/servlet/ShortcutServlet.java

@@ -31,6 +31,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
+import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
@@ -39,8 +40,8 @@ import password.pwm.ldap.permission.UserPermissionType;
 import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.stats.StatisticsClient;
+import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
-import password.pwm.util.java.MiscUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -50,12 +51,11 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 @WebServlet(
 @WebServlet(
@@ -65,7 +65,7 @@ import java.util.stream.Collectors;
                 PwmConstants.URL_PREFIX_PRIVATE + "/Shortcuts",
                 PwmConstants.URL_PREFIX_PRIVATE + "/Shortcuts",
         }
         }
 )
 )
-public class ShortcutServlet extends AbstractPwmServlet
+public class ShortcutServlet extends ControlledPwmServlet
 {
 {
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass( ShortcutServlet.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( ShortcutServlet.class );
@@ -89,21 +89,33 @@ public class ShortcutServlet extends AbstractPwmServlet
     }
     }
 
 
     @Override
     @Override
-    protected void processAction( final PwmRequest pwmRequest )
-            throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
+    public Class<? extends ProcessAction> getProcessActionsClass()
     {
     {
-        final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
+        return ShortcutAction.class;
+    }
+
+    @Override
+    protected void nextStep( final PwmRequest pwmRequest )
+            throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
+    {
+        forwardToJsp( pwmRequest, getBean( pwmRequest ) );
 
 
+    }
+
+    @Override
+    public ProcessStatus preProcessCheck( final PwmRequest pwmRequest )
+            throws PwmUnrecoverableException, IOException, ServletException
+    {
+        final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         if ( !pwmDomain.getConfig().readSettingAsBoolean( PwmSetting.SHORTCUT_ENABLE ) )
         if ( !pwmDomain.getConfig().readSettingAsBoolean( PwmSetting.SHORTCUT_ENABLE ) )
         {
         {
             pwmRequest.respondWithError( PwmError.ERROR_SERVICE_NOT_AVAILABLE.toInfo() );
             pwmRequest.respondWithError( PwmError.ERROR_SERVICE_NOT_AVAILABLE.toInfo() );
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
-        final ShortcutsBean shortcutsBean = pwmDomain.getSessionStateService().getBean( pwmRequest, ShortcutsBean.class );
+        final ShortcutsBean shortcutsBean = getBean( pwmRequest );
         if ( shortcutsBean.getVisibleItems() == null )
         if ( shortcutsBean.getVisibleItems() == null )
         {
         {
-            LOGGER.debug( pwmRequest, () -> "building visible shortcut list for user" );
             final Map<String, ShortcutItem> visibleItems = figureVisibleShortcuts( pwmRequest );
             final Map<String, ShortcutItem> visibleItems = figureVisibleShortcuts( pwmRequest );
             shortcutsBean.setVisibleItems( visibleItems );
             shortcutsBean.setVisibleItems( visibleItems );
         }
         }
@@ -112,22 +124,43 @@ public class ShortcutServlet extends AbstractPwmServlet
             LOGGER.trace( pwmRequest, () -> "using cashed shortcut values" );
             LOGGER.trace( pwmRequest, () -> "using cashed shortcut values" );
         }
         }
 
 
-        final Optional<ShortcutAction> action = readProcessAction( pwmRequest );
-        if ( action.isPresent() )
+        return ProcessStatus.Continue;
+    }
+
+    public ShortcutsBean getBean( final PwmRequest pwmRequest )
+            throws PwmUnrecoverableException
+    {
+        final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
+        return pwmDomain.getSessionStateService().getBean( pwmRequest, ShortcutsBean.class );
+
+    }
+
+    @ActionHandler( action = "selectShortcut" )
+    public ProcessStatus processSelectShortcutRequest(
+            final PwmRequest pwmRequest
+    )
+            throws PwmUnrecoverableException, IOException, ServletException
+    {
+        final ShortcutsBean shortcutsBean = getBean( pwmRequest );
+        final PwmSession pwmSession = pwmRequest.getPwmSession();
+
+        final String link = pwmRequest.readParameterAsString( "link" );
+        final Map<String, ShortcutItem> visibleItems = shortcutsBean.getVisibleItems();
+
+        if ( link != null && visibleItems.containsKey( link ) )
         {
         {
-            pwmRequest.validatePwmFormID();
-            switch ( action.get() )
-            {
-                case selectShortcut:
-                    handleUserSelection( pwmRequest, shortcutsBean );
-                    return;
+            final ShortcutItem item = visibleItems.get( link );
 
 
-                default:
-                    MiscUtil.unhandledSwitchStatement( action.get() );
-            }
+            StatisticsClient.incrementStat( pwmRequest, Statistic.SHORTCUTS_SELECTED );
+            LOGGER.trace( pwmRequest, () -> "shortcut link selected: " + link + ", setting link for 'forwardURL' to " + item.getShortcutURI() );
+            pwmSession.getSessionStateBean().setForwardURL( item.getShortcutURI().toString() );
+
+            pwmRequest.getPwmResponse().sendRedirectToContinue();
+            return ProcessStatus.Halt;
         }
         }
 
 
-        forwardToJsp( pwmRequest, shortcutsBean );
+        LOGGER.error( pwmRequest, () -> "unknown/unexpected link requested to " + link );
+        return ProcessStatus.Continue;
     }
     }
 
 
     private void forwardToJsp( final PwmRequest pwmRequest, final ShortcutsBean shortcutsBean )
     private void forwardToJsp( final PwmRequest pwmRequest, final ShortcutsBean shortcutsBean )
@@ -139,96 +172,85 @@ public class ShortcutServlet extends AbstractPwmServlet
     }
     }
 
 
     /**
     /**
-     * Loop through each configured shortcut setting to determine if the shortcut is is able to the user pwmSession.
+     * Loop through each configured shortcut setting to determine if the shortcut is able to the user pwmSession.
      */
      */
     private static Map<String, ShortcutItem> figureVisibleShortcuts(
     private static Map<String, ShortcutItem> figureVisibleShortcuts(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
-            throws PwmUnrecoverableException
     {
     {
-        final Collection<String> configValues = pwmRequest.getDomainConfig().readSettingAsLocalizedStringArray( PwmSetting.SHORTCUT_ITEMS, pwmRequest.getLocale() );
+        LOGGER.debug( pwmRequest, () -> "building visible shortcut list for user" );
 
 
-        final Set<String> labelsFromHeader = new HashSet<>();
-        {
-            final Map<String, List<String>> headerValueMap = pwmRequest.readHeaderValuesMap();
-            final List<String> interestedHeaderNames = pwmRequest.getDomainConfig().readSettingAsStringArray( PwmSetting.SHORTCUT_HEADER_NAMES );
+        final List<String> interestedHeaderNames = pwmRequest.getDomainConfig().readSettingAsStringArray( PwmSetting.SHORTCUT_HEADER_NAMES );
+        final Set<String> labelsFromHeader = pwmRequest.readHeaderValuesMap().entrySet().stream()
+                .filter( entry -> StringUtil.caseIgnoreContains(  interestedHeaderNames, entry.getKey() ) )
+                .flatMap( stringListEntry -> stringListEntry.getValue().stream() )
+                .flatMap( value -> StringUtil.tokenizeString( value, "," ).stream() )
+                .collect( Collectors.toUnmodifiableSet() );
 
 
-            for ( final Map.Entry<String, List<String>> entry : headerValueMap.entrySet() )
-            {
-                final String headerName = entry.getKey();
-                if ( interestedHeaderNames.contains( headerName ) )
-                {
-                    for ( final String loopValues : entry.getValue() )
-                    {
-                        labelsFromHeader.addAll( StringUtil.tokenizeString( loopValues, "," ) );
-                    }
-                }
-            }
+        if ( !interestedHeaderNames.isEmpty() )
+        {
+            LOGGER.trace( pwmRequest, () -> "examined headers '" + StringUtil.collectionToString( interestedHeaderNames )
+                    + "' and found these values: '" + StringUtil.collectionToString( labelsFromHeader ) + "'" );
         }
         }
 
 
+        final Collection<String> configValues = pwmRequest.getDomainConfig().readSettingAsLocalizedStringArray( PwmSetting.SHORTCUT_ITEMS, pwmRequest.getLocale() );
         final List<ShortcutItem> configuredItems = configValues.stream()
         final List<ShortcutItem> configuredItems = configValues.stream()
                 .map( ShortcutItem::parsePwmConfigInput )
                 .map( ShortcutItem::parsePwmConfigInput )
                 .collect( Collectors.toUnmodifiableList() );
                 .collect( Collectors.toUnmodifiableList() );
 
 
-        final Map<String, ShortcutItem> visibleItems = new LinkedHashMap<>( configuredItems.size() );
 
 
-        if ( !labelsFromHeader.isEmpty() )
-        {
-            LOGGER.trace( () -> "detected the following labels from headers: " + StringUtil.collectionToString( labelsFromHeader, "," ) );
-            visibleItems.keySet().retainAll( labelsFromHeader );
-        }
-        else
-        {
-            for ( final ShortcutItem item : configuredItems )
-            {
-                final UserIdentity userIdentity = pwmRequest.getPwmSession().getUserInfo().getUserIdentity();
-
-                final UserPermission userPermission = UserPermission.builder()
-                        .type( UserPermissionType.ldapQuery )
-                        .ldapQuery( item.getLdapQuery() )
-                        .ldapBase( userIdentity.getLdapProfileID() )
-                        .build();
-
-                final boolean queryMatch = UserPermissionUtility.testUserPermission(
-                        pwmRequest.getPwmRequestContext(),
-                        pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
-                        userPermission
-                );
-
-                if ( queryMatch )
-                {
-                    visibleItems.put( item.getLabel(), item );
-                }
-            }
-        }
+        final Map<String, ShortcutItem> visibleItems = Collections.unmodifiableMap( configuredItems.stream()
+                .filter( item -> checkItemMatch( pwmRequest, labelsFromHeader, item ) )
+                .collect( CollectionUtil.collectorToLinkedMap(
+                        ShortcutItem::getLabel,
+                        Function.identity() ) ) );
+
+        LOGGER.debug( pwmRequest, () -> "built visible shortcut list for user: '" + StringUtil.collectionToString( visibleItems.keySet() ) + "'" );
 
 
         return visibleItems;
         return visibleItems;
     }
     }
 
 
-    private void handleUserSelection(
+    private static boolean checkItemMatch(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
-            final ShortcutsBean shortcutsBean
+            final Set<String> labelsFromHeader,
+            final ShortcutItem item
     )
     )
-            throws PwmUnrecoverableException,  IOException, ServletException
     {
     {
-        final PwmSession pwmSession = pwmRequest.getPwmSession();
+        if ( StringUtil.caseIgnoreContains( labelsFromHeader, item.getLabel() ) )
+        {
+            LOGGER.trace( () -> "adding the shortcut item '" + item.getLabel() + "' due to presence of configured headers in request" );
+            return true;
+        }
 
 
-        final String link = pwmRequest.readParameterAsString( "link" );
-        final Map<String, ShortcutItem> visibleItems = shortcutsBean.getVisibleItems();
+        final UserIdentity userIdentity = pwmRequest.getPwmSession().getUserInfo().getUserIdentity();
 
 
-        if ( link != null && visibleItems.containsKey( link ) )
+        final UserPermission userPermission = UserPermission.builder()
+                .type( UserPermissionType.ldapQuery )
+                .ldapQuery( item.getLdapQuery() )
+                .ldapBase( userIdentity.getUserDN() )
+                .build();
+
+        try
         {
         {
-            final ShortcutItem item = visibleItems.get( link );
+            final boolean match = UserPermissionUtility.testUserPermission(
+                    pwmRequest.getPwmRequestContext(),
+                    pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
+                    userPermission
+            );
 
 
-            StatisticsClient.incrementStat( pwmRequest, Statistic.SHORTCUTS_SELECTED );
-            LOGGER.trace( pwmRequest, () -> "shortcut link selected: " + link + ", setting link for 'forwardURL' to " + item.getShortcutURI() );
-            pwmSession.getSessionStateBean().setForwardURL( item.getShortcutURI().toString() );
+            if ( match )
+            {
+                LOGGER.trace( pwmRequest, () -> "adding the shortcut item '" + item.getLabel() + "' due to ldap query match" );
+            }
 
 
-            pwmRequest.getPwmResponse().sendRedirectToContinue();
-            return;
+            return match;
+        }
+        catch ( final PwmUnrecoverableException e )
+        {
+            LOGGER.trace( pwmRequest, () -> "error during ldap user permission test of shortcut label '" + item.getLabel() + "', error: " + e.getMessage() );
         }
         }
 
 
-        LOGGER.error( pwmRequest, () -> "unknown/unexpected link requested to " + link );
-        pwmRequest.forwardToJsp( JspUrl.SHORTCUT );
+        return false;
     }
     }
+
 }
 }

+ 2 - 3
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -3196,9 +3196,8 @@
         </default>
         </default>
     </setting>
     </setting>
     <setting hidden="false" key="shortcut.httpHeaders" level="1">
     <setting hidden="false" key="shortcut.httpHeaders" level="1">
-        <default>
-            <value><![CDATA[X-Shortcuts]]></value>
-        </default>
+        <example>X-Shortcuts</example>
+        <default/>
     </setting>
     </setting>
     <setting hidden="false" key="shortcut.newWindow" level="1">
     <setting hidden="false" key="shortcut.newWindow" level="1">
         <default>
         <default>

+ 1 - 1
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -724,7 +724,7 @@ Setting_Description_security.sso.authHeaderName=Specify the name of the HTTP hea
 Setting_Description_session.maxSeconds=Specify the maximum duration of a session (in seconds).  Having a maximum session lifetime prevents certain types of long-term session fixation attacks.
 Setting_Description_session.maxSeconds=Specify the maximum duration of a session (in seconds).  Having a maximum session lifetime prevents certain types of long-term session fixation attacks.
 Setting_Description_security.certificate.validationMode=Specify how outbound SSL/TLS certificate validation will be performed by @PwmAppName@.
 Setting_Description_security.certificate.validationMode=Specify how outbound SSL/TLS certificate validation will be performed by @PwmAppName@.
 Setting_Description_shortcut.enable=Enable this option to enable the shortcuts module.
 Setting_Description_shortcut.enable=Enable this option to enable the shortcuts module.
-Setting_Description_shortcut.httpHeaders=Specify the HTTP Headers to use to control the visible list of shortcuts.  If this header is present, @PwmAppName@ uses these values to determine which of the configured shortcuts are available to a user.  The values must correspond to the label values specified as part of the shortcut items.  When this header is present, @PwmAppName@ does not use the ldapQuery portion of the shortcut items and instead displays the shortcuts only if the label is present in the header.<br/><br/>Values can be set in multiple headers, or by comma separating the values.<br/><br/>A blank value disables this feature.
+Setting_Description_shortcut.httpHeaders=Specify the HTTP Headers to use to control the visible list of shortcuts.  If this header is present, @PwmAppName@ uses these values to determine which of the configured shortcuts are available to a user.  The values must correspond to the label values specified as part of the shortcut items.  When this header is present, @PwmAppName@ does not use the ldapQuery portion of the shortcut items and instead displays the shortcuts only if the label is present in the header.<br/><br/>Values can be set in multiple headers, or by comma separating the values.<br/><br/>A blank value disables this feature.<p><b>Warning:</b>Only enable this feature if an upstream proxy/gateway server controls this header value.  Otherwise it may be possible for a client to inject this value and view shortcuts not otherwise visible.</p>
 Setting_Description_shortcut.items=Specify the list of available shortcuts.<br/><br/>Format\: <b>label\:\:url\:\:ldapQuery\:\:description</b><table style\="width\: 400px"><tr><td>label</td><td>Label to show to users</td></tr><tr><td>ldapQuery</td><td>Valid LDAP syntax style query, if the user matches this query, then @PwmAppName@ shows the shortcut to the users.</td></tr><tr><td>url</td><td>http shortcut to direct the user to</td></tr><tr><td>description</td><td>Long description of the shortcut</td></tr></table>
 Setting_Description_shortcut.items=Specify the list of available shortcuts.<br/><br/>Format\: <b>label\:\:url\:\:ldapQuery\:\:description</b><table style\="width\: 400px"><tr><td>label</td><td>Label to show to users</td></tr><tr><td>ldapQuery</td><td>Valid LDAP syntax style query, if the user matches this query, then @PwmAppName@ shows the shortcut to the users.</td></tr><tr><td>url</td><td>http shortcut to direct the user to</td></tr><tr><td>description</td><td>Long description of the shortcut</td></tr></table>
 Setting_Description_shortcut.newWindow=Enable this option to launch shortcuts in a new window (or tab).
 Setting_Description_shortcut.newWindow=Enable this option to launch shortcuts in a new window (or tab).
 Setting_Description_sms.activation.message=Specify the message text of the SMS @PwmAppName@ sends after a succesful activation.
 Setting_Description_sms.activation.message=Specify the message text of the SMS @PwmAppName@ sends after a succesful activation.