UserMatchViewerFunction.java 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2020 The PWM Project
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. package password.pwm.config.function;
  21. import com.novell.ldapchai.ChaiEntry;
  22. import com.novell.ldapchai.exception.ChaiUnavailableException;
  23. import com.novell.ldapchai.provider.ChaiProvider;
  24. import lombok.Builder;
  25. import lombok.Value;
  26. import password.pwm.AppProperty;
  27. import password.pwm.PwmApplication;
  28. import password.pwm.PwmDomain;
  29. import password.pwm.bean.SessionLabel;
  30. import password.pwm.bean.UserIdentity;
  31. import password.pwm.config.AppConfig;
  32. import password.pwm.config.SettingUIFunction;
  33. import password.pwm.config.stored.StoredConfigKey;
  34. import password.pwm.config.stored.StoredConfiguration;
  35. import password.pwm.config.stored.StoredConfigurationModifier;
  36. import password.pwm.config.stored.StoredConfigurationUtil;
  37. import password.pwm.config.value.StoredValue;
  38. import password.pwm.config.value.ValueTypeConverter;
  39. import password.pwm.config.value.data.UserPermission;
  40. import password.pwm.error.ErrorInformation;
  41. import password.pwm.error.PwmError;
  42. import password.pwm.error.PwmOperationalException;
  43. import password.pwm.error.PwmUnrecoverableException;
  44. import password.pwm.http.PwmRequest;
  45. import password.pwm.i18n.Display;
  46. import password.pwm.ldap.permission.UserPermissionType;
  47. import password.pwm.ldap.permission.UserPermissionUtility;
  48. import password.pwm.util.i18n.LocaleHelper;
  49. import password.pwm.util.java.CollectionUtil;
  50. import password.pwm.util.java.TimeDuration;
  51. import password.pwm.util.logging.PwmLogger;
  52. import java.io.Serializable;
  53. import java.time.Instant;
  54. import java.util.ArrayList;
  55. import java.util.Collection;
  56. import java.util.Collections;
  57. import java.util.Iterator;
  58. import java.util.LinkedHashSet;
  59. import java.util.List;
  60. import java.util.Set;
  61. public class UserMatchViewerFunction implements SettingUIFunction
  62. {
  63. private static final PwmLogger LOGGER = PwmLogger.forClass( UserMatchViewerFunction.class );
  64. @Override
  65. public Serializable provideFunction(
  66. final PwmRequest pwmRequest,
  67. final StoredConfigurationModifier storedConfiguration,
  68. final StoredConfigKey key,
  69. final String extraData )
  70. throws Exception
  71. {
  72. final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
  73. final Instant startSearchTime = Instant.now();
  74. final int maxResultSize = Integer.parseInt( pwmDomain.getConfig().readAppProperty( AppProperty.CONFIG_EDITOR_USER_PERMISSION_MATCH_LIMIT ) );
  75. final Collection<UserIdentity> users = discoverMatchingUsers( pwmDomain, maxResultSize, storedConfiguration.newStoredConfiguration(), key );
  76. final TimeDuration searchDuration = TimeDuration.fromCurrent( startSearchTime );
  77. final String message = LocaleHelper.getLocalizedMessage(
  78. Display.Display_SearchResultsInfo, pwmRequest,
  79. String.valueOf( users.size() ),
  80. searchDuration.asLongString( pwmRequest.getLocale() ) );
  81. final boolean sizeExceeded = users.size() >= maxResultSize;
  82. return UserMatchViewerResults.builder()
  83. .users( users )
  84. .searchOperationSummary( message )
  85. .sizeExceeded( sizeExceeded )
  86. .build();
  87. }
  88. public List<UserIdentity> discoverMatchingUsers(
  89. final PwmDomain pwmDomain,
  90. final int maxResultSize,
  91. final StoredConfiguration storedConfiguration,
  92. final StoredConfigKey key
  93. )
  94. throws Exception
  95. {
  96. final AppConfig config = new AppConfig( storedConfiguration );
  97. final PwmApplication tempApplication = PwmApplication.createPwmApplication( pwmDomain.getPwmApplication().getPwmEnvironment().makeRuntimeInstance( config ) );
  98. final StoredValue storedValue = StoredConfigurationUtil.getValueOrDefault( storedConfiguration, key );
  99. final List<UserPermission> permissions = ValueTypeConverter.valueToUserPermissions( storedValue );
  100. final PwmDomain tempDomain = tempApplication.domains().get( key.getDomainID() );
  101. validateUserPermissionLdapValues( tempDomain, permissions );
  102. final int maxSearchSeconds = Integer.parseInt( pwmDomain.getConfig().readAppProperty( AppProperty.CONFIG_EDITOR_USER_PERMISSION_TIMEOUT_SECONDS ) );
  103. final TimeDuration maxSearchTime = TimeDuration.of( maxSearchSeconds, TimeDuration.Unit.SECONDS );
  104. final Iterator<UserIdentity> matches = UserPermissionUtility.discoverMatchingUsers( tempDomain, permissions, SessionLabel.SYSTEM_LABEL, maxResultSize, maxSearchTime );
  105. final List<UserIdentity> sortedResults = new ArrayList<>( CollectionUtil.iteratorToList( matches ) );
  106. Collections.sort( sortedResults );
  107. return Collections.unmodifiableList ( sortedResults );
  108. }
  109. private static void validateUserPermissionLdapValues(
  110. final PwmDomain pwmDomain,
  111. final List<UserPermission> permissions
  112. )
  113. throws PwmUnrecoverableException, PwmOperationalException
  114. {
  115. for ( final UserPermission userPermission : permissions )
  116. {
  117. if ( userPermission.getType() == UserPermissionType.ldapQuery )
  118. {
  119. if ( userPermission.getLdapBase() != null && !userPermission.getLdapBase().isEmpty() )
  120. {
  121. testIfLdapDNIsValid( pwmDomain, userPermission.getLdapBase(), userPermission.getLdapProfileID() );
  122. }
  123. }
  124. else if ( userPermission.getType() == UserPermissionType.ldapGroup )
  125. {
  126. testIfLdapDNIsValid( pwmDomain, userPermission.getLdapBase(), userPermission.getLdapProfileID() );
  127. }
  128. }
  129. }
  130. private static void testIfLdapDNIsValid( final PwmDomain pwmDomain, final String baseDN, final String profileID )
  131. throws PwmOperationalException, PwmUnrecoverableException
  132. {
  133. final Set<String> profileIDsToTest = new LinkedHashSet<>();
  134. if ( UserPermissionUtility.isAllProfiles( profileID ) )
  135. {
  136. profileIDsToTest.addAll( pwmDomain.getConfig().getLdapProfiles().keySet() );
  137. }
  138. else
  139. {
  140. profileIDsToTest.add( profileID );
  141. }
  142. if ( profileIDsToTest.isEmpty() )
  143. {
  144. throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_NO_PROFILE_ASSIGNED, "invalid ldap profile" ) );
  145. }
  146. for ( final String loopID : profileIDsToTest )
  147. {
  148. ChaiEntry chaiEntry = null;
  149. try
  150. {
  151. final ChaiProvider proxiedProvider = pwmDomain.getProxyChaiProvider( loopID );
  152. chaiEntry = proxiedProvider.getEntryFactory().newChaiEntry( baseDN );
  153. }
  154. catch ( final Exception e )
  155. {
  156. LOGGER.error( () -> "error while testing entry DN for profile '" + profileID + "', error:" + profileID );
  157. }
  158. try
  159. {
  160. if ( chaiEntry != null && !chaiEntry.exists() )
  161. {
  162. final String errorMsg = "entry DN '" + baseDN + "' is not valid for profile " + loopID;
  163. throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_LDAP_DATA_ERROR, errorMsg ) );
  164. }
  165. }
  166. catch ( final ChaiUnavailableException e )
  167. {
  168. throw PwmUnrecoverableException.fromChaiException( e );
  169. }
  170. }
  171. }
  172. @Value
  173. @Builder
  174. public static class UserMatchViewerResults implements Serializable
  175. {
  176. private Collection<UserIdentity> users;
  177. private boolean sizeExceeded;
  178. private String searchOperationSummary;
  179. }
  180. }