LDAPAuthenticationRequest.java 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2018 The PWM Project
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. package password.pwm.ldap.auth;
  23. import com.novell.ldapchai.ChaiConstant;
  24. import com.novell.ldapchai.ChaiUser;
  25. import com.novell.ldapchai.exception.ChaiError;
  26. import com.novell.ldapchai.exception.ChaiException;
  27. import com.novell.ldapchai.exception.ChaiOperationException;
  28. import com.novell.ldapchai.exception.ChaiUnavailableException;
  29. import com.novell.ldapchai.exception.ImpossiblePasswordPolicyException;
  30. import com.novell.ldapchai.impl.oracleds.entry.OracleDSEntries;
  31. import com.novell.ldapchai.provider.ChaiProvider;
  32. import com.novell.ldapchai.provider.ChaiSetting;
  33. import com.novell.ldapchai.provider.DirectoryVendor;
  34. import password.pwm.AppProperty;
  35. import password.pwm.PwmApplication;
  36. import password.pwm.PwmConstants;
  37. import password.pwm.bean.SessionLabel;
  38. import password.pwm.bean.UserIdentity;
  39. import password.pwm.config.PwmSetting;
  40. import password.pwm.config.profile.LdapProfile;
  41. import password.pwm.config.profile.PwmPasswordPolicy;
  42. import password.pwm.error.ErrorInformation;
  43. import password.pwm.error.PwmError;
  44. import password.pwm.error.PwmOperationalException;
  45. import password.pwm.error.PwmUnrecoverableException;
  46. import password.pwm.http.servlet.forgottenpw.ForgottenPasswordUtil;
  47. import password.pwm.ldap.LdapOperationsHelper;
  48. import password.pwm.svc.event.AuditEvent;
  49. import password.pwm.svc.event.AuditRecord;
  50. import password.pwm.svc.event.AuditRecordFactory;
  51. import password.pwm.svc.intruder.IntruderManager;
  52. import password.pwm.svc.intruder.RecordType;
  53. import password.pwm.svc.stats.AvgStatistic;
  54. import password.pwm.svc.stats.EpsStatistic;
  55. import password.pwm.svc.stats.Statistic;
  56. import password.pwm.svc.stats.StatisticsManager;
  57. import password.pwm.util.PasswordData;
  58. import password.pwm.util.RandomPasswordGenerator;
  59. import password.pwm.util.java.AtomicLoopIntIncrementer;
  60. import password.pwm.util.java.JavaHelper;
  61. import password.pwm.util.java.TimeDuration;
  62. import password.pwm.util.logging.PwmLogLevel;
  63. import password.pwm.util.logging.PwmLogger;
  64. import password.pwm.util.macro.MacroMachine;
  65. import password.pwm.util.operations.PasswordUtility;
  66. import java.time.Instant;
  67. import java.util.Collections;
  68. import java.util.HashSet;
  69. import java.util.Set;
  70. import java.util.function.Supplier;
  71. class LDAPAuthenticationRequest implements AuthenticationRequest
  72. {
  73. private static final PwmLogger LOGGER = PwmLogger.forClass( LDAPAuthenticationRequest.class );
  74. private static final String ORACLE_ATTR_PW_ALLOW_CHG_TIME = "passwordAllowChangeTime";
  75. private final PwmApplication pwmApplication;
  76. private final SessionLabel sessionLabel;
  77. private final UserIdentity userIdentity;
  78. private final AuthenticationType requestedAuthType;
  79. private final PwmAuthenticationSource authenticationSource;
  80. private ChaiProvider userProvider;
  81. private AuthenticationStrategy strategy = AuthenticationStrategy.BIND;
  82. private Instant startTime;
  83. private static final AtomicLoopIntIncrementer OPERATION_COUNTER = new AtomicLoopIntIncrementer();
  84. private final int operationNumber;
  85. LDAPAuthenticationRequest(
  86. final PwmApplication pwmApplication,
  87. final SessionLabel sessionLabel,
  88. final UserIdentity userIdentity,
  89. final AuthenticationType requestedAuthType,
  90. final PwmAuthenticationSource authenticationSource
  91. )
  92. {
  93. this.pwmApplication = pwmApplication;
  94. this.sessionLabel = sessionLabel;
  95. this.userIdentity = userIdentity;
  96. this.requestedAuthType = requestedAuthType;
  97. this.authenticationSource = authenticationSource;
  98. this.operationNumber = OPERATION_COUNTER.next();
  99. }
  100. static AuthenticationRequest createLDAPAuthenticationRequest(
  101. final PwmApplication pwmApplication,
  102. final SessionLabel sessionLabel,
  103. final UserIdentity userIdentity,
  104. final AuthenticationType requestedAuthType,
  105. final PwmAuthenticationSource authenticationSource
  106. )
  107. {
  108. return new LDAPAuthenticationRequest( pwmApplication, sessionLabel, userIdentity, requestedAuthType, authenticationSource );
  109. }
  110. @Override
  111. public AuthenticationResult authUsingUnknownPw( )
  112. throws ChaiUnavailableException, PwmUnrecoverableException
  113. {
  114. initialize();
  115. log( PwmLogLevel.TRACE, () -> "beginning authentication using unknown password procedure" );
  116. PasswordData userPassword = null;
  117. final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN );
  118. if ( configAlwaysUseProxy )
  119. {
  120. strategy = AuthenticationStrategy.ADMIN_PROXY;
  121. }
  122. else
  123. {
  124. userPassword = learnUserPassword();
  125. if ( userPassword != null )
  126. {
  127. strategy = AuthenticationStrategy.READ_THEN_BIND;
  128. }
  129. else
  130. {
  131. userPassword = setTempUserPassword();
  132. if ( userPassword != null )
  133. {
  134. strategy = AuthenticationStrategy.WRITE_THEN_BIND;
  135. }
  136. }
  137. }
  138. if ( userPassword == null && requestedAuthType == AuthenticationType.AUTH_WITHOUT_PASSWORD )
  139. {
  140. log( PwmLogLevel.TRACE, () -> "unable to learn password or connect using proxy, thus authenticating user without a password" );
  141. return authenticateUserWithoutPassword();
  142. }
  143. try
  144. {
  145. return authenticateUserImpl( userPassword );
  146. }
  147. catch ( PwmOperationalException e )
  148. {
  149. if ( strategy == AuthenticationStrategy.READ_THEN_BIND )
  150. {
  151. final String errorStr = "unable to authenticate with password read from directory, check proxy rights, ldap logs; error: " + e.getMessage();
  152. throw new PwmUnrecoverableException(
  153. new ErrorInformation( PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr ) );
  154. }
  155. else if ( strategy == AuthenticationStrategy.WRITE_THEN_BIND )
  156. {
  157. final String errorStr = "unable to authenticate with temporary password, check proxy rights, ldap logs; error: " + e.getMessage();
  158. throw new PwmUnrecoverableException(
  159. new ErrorInformation( PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr ) );
  160. }
  161. throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_INTERNAL, "unable to authenticate via authWithUnknownPw method: " + e.getMessage() ) );
  162. }
  163. }
  164. @Override
  165. public AuthenticationResult authenticateUser( final PasswordData password )
  166. throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
  167. {
  168. initialize();
  169. return authenticateUserImpl( password );
  170. }
  171. private AuthenticationResult authenticateUserWithoutPassword() throws PwmUnrecoverableException
  172. {
  173. if ( !Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.AUTH_ALLOW_SSO_WITH_UNKNOWN_PW ) ) )
  174. {
  175. log( PwmLogLevel.TRACE, () -> "AppProperty " + AppProperty.AUTH_ALLOW_SSO_WITH_UNKNOWN_PW + " is not true, thus prohibiting auth with unknown password" );
  176. throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_INTERNAL, "no available unknown-pw authentication method" ) );
  177. }
  178. preAuthenticationChecks();
  179. final AuthenticationResult authenticationResult = new AuthenticationResult(
  180. null,
  181. AuthenticationType.AUTH_WITHOUT_PASSWORD,
  182. null
  183. );
  184. postAuthenticationSteps( authenticationResult, false );
  185. return authenticationResult;
  186. }
  187. private void preAuthenticationChecks() throws PwmUnrecoverableException
  188. {
  189. log( PwmLogLevel.DEBUG, () -> "preparing to authenticate user using authenticationType=" + this.requestedAuthType + " using strategy " + this.strategy );
  190. final IntruderManager intruderManager = pwmApplication.getIntruderManager();
  191. intruderManager.convenience().checkUserIdentity( userIdentity );
  192. intruderManager.check( RecordType.ADDRESS, sessionLabel.getSrcAddress() );
  193. // verify user is not account disabled
  194. AuthenticationUtility.checkIfUserEligibleToAuthentication( pwmApplication, userIdentity );
  195. }
  196. private AuthenticationResult authenticateUserImpl(
  197. final PasswordData password
  198. )
  199. throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException
  200. {
  201. if ( startTime == null )
  202. {
  203. startTime = Instant.now();
  204. }
  205. preAuthenticationChecks();
  206. boolean allowBindAsUser = true;
  207. if ( strategy == AuthenticationStrategy.ADMIN_PROXY )
  208. {
  209. allowBindAsUser = false;
  210. }
  211. if ( allowBindAsUser )
  212. {
  213. try
  214. {
  215. testCredentials( userIdentity, password );
  216. }
  217. catch ( PwmOperationalException e )
  218. {
  219. boolean permitAuthDespiteError = false;
  220. final DirectoryVendor vendor = pwmApplication.getProxyChaiProvider(
  221. userIdentity.getLdapProfileID() ).getDirectoryVendor();
  222. if ( PwmError.PASSWORD_NEW_PASSWORD_REQUIRED == e.getError() )
  223. {
  224. if ( vendor == DirectoryVendor.ACTIVE_DIRECTORY )
  225. {
  226. if ( pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD ) )
  227. {
  228. log( PwmLogLevel.DEBUG,
  229. () -> "auth bind failed, but will allow login due to 'must change password on next login AD error', error: "
  230. + e.getErrorInformation().toDebugStr() );
  231. allowBindAsUser = false;
  232. permitAuthDespiteError = true;
  233. }
  234. }
  235. else if ( vendor == DirectoryVendor.ORACLE_DS )
  236. {
  237. if ( pwmApplication.getConfig().readSettingAsBoolean(
  238. PwmSetting.ORACLE_DS_ALLOW_AUTH_REQUIRE_NEW_PWD ) )
  239. {
  240. log( PwmLogLevel.DEBUG,
  241. () -> "auth bind failed, but will allow login due to 'pwdReset' user attribute, error: "
  242. + e.getErrorInformation().toDebugStr() );
  243. allowBindAsUser = false;
  244. permitAuthDespiteError = true;
  245. }
  246. }
  247. }
  248. else if ( PwmError.PASSWORD_EXPIRED == e.getError() )
  249. {
  250. // handle ad case where password is expired
  251. if ( vendor == DirectoryVendor.ACTIVE_DIRECTORY )
  252. {
  253. if ( pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD ) )
  254. {
  255. if ( !pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.AD_ALLOW_AUTH_EXPIRED ) )
  256. {
  257. throw e;
  258. }
  259. log( PwmLogLevel.DEBUG,
  260. () -> "auth bind failed, but will allow login due to 'password expired AD error', error: " + e.getErrorInformation().toDebugStr() );
  261. allowBindAsUser = false;
  262. permitAuthDespiteError = true;
  263. }
  264. }
  265. }
  266. if ( !permitAuthDespiteError )
  267. {
  268. // auth failed, presumably due to wrong password.
  269. StatisticsManager.incrementStat( pwmApplication, Statistic.AUTHENTICATION_FAILURES );
  270. throw e;
  271. }
  272. }
  273. }
  274. final AuthenticationType returnAuthType;
  275. if ( !allowBindAsUser )
  276. {
  277. returnAuthType = AuthenticationType.AUTH_BIND_INHIBIT;
  278. }
  279. else
  280. {
  281. if ( requestedAuthType == null )
  282. {
  283. returnAuthType = AuthenticationType.AUTHENTICATED;
  284. }
  285. else
  286. {
  287. if ( requestedAuthType == AuthenticationType.AUTH_WITHOUT_PASSWORD )
  288. {
  289. returnAuthType = AuthenticationType.AUTHENTICATED;
  290. }
  291. else if ( requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE )
  292. {
  293. returnAuthType = AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
  294. }
  295. else
  296. {
  297. returnAuthType = requestedAuthType;
  298. }
  299. }
  300. }
  301. final boolean useProxy = determineIfLdapProxyNeeded( returnAuthType, password );
  302. final ChaiProvider returnProvider = useProxy ? makeProxyProvider() : userProvider;
  303. final AuthenticationResult authenticationResult = new AuthenticationResult( returnProvider, returnAuthType, password );
  304. postAuthenticationSteps( authenticationResult, useProxy );
  305. return authenticationResult;
  306. }
  307. private void postAuthenticationSteps(
  308. final AuthenticationResult authenticationResult,
  309. final boolean usingProxy
  310. )
  311. throws PwmUnrecoverableException
  312. {
  313. final StatisticsManager statisticsManager = pwmApplication.getStatisticsManager();
  314. statisticsManager.incrementValue( Statistic.AUTHENTICATIONS );
  315. statisticsManager.updateEps( EpsStatistic.AUTHENTICATION, 1 );
  316. statisticsManager.updateAverageValue( AvgStatistic.AVG_AUTHENTICATION_TIME,
  317. TimeDuration.fromCurrent( startTime ).asMillis() );
  318. log( PwmLogLevel.DEBUG, () -> "successful ldap authentication for " + userIdentity
  319. + " (" + TimeDuration.fromCurrent( startTime ).asCompactString() + ")"
  320. + " type: " + authenticationResult.getAuthenticationType() + ", using strategy " + strategy
  321. + ", using proxy connection: " + usingProxy
  322. + ", returning bind dn: "
  323. + ( authenticationResult.getUserProvider() == null
  324. ? "none"
  325. : authenticationResult.getUserProvider().getChaiConfiguration().getSetting( ChaiSetting.BIND_DN ) ) );
  326. final MacroMachine macroMachine = MacroMachine.forUser( pwmApplication, PwmConstants.DEFAULT_LOCALE, sessionLabel, userIdentity );
  327. final AuditRecord auditRecord = new AuditRecordFactory( pwmApplication, macroMachine ).createUserAuditRecord(
  328. AuditEvent.AUTHENTICATE,
  329. this.userIdentity,
  330. makeAuditLogMessage( authenticationResult.getAuthenticationType() ),
  331. sessionLabel.getSrcAddress(),
  332. sessionLabel.getSrcHostname()
  333. );
  334. pwmApplication.getAuditManager().submit( auditRecord );
  335. pwmApplication.getSessionTrackService().addRecentLogin( userIdentity );
  336. }
  337. private void initialize( )
  338. {
  339. if ( startTime != null )
  340. {
  341. throw new IllegalStateException( "AuthenticationRequest can not be used more than once" );
  342. }
  343. startTime = Instant.now();
  344. }
  345. private void testCredentials(
  346. final UserIdentity userIdentity,
  347. final PasswordData password
  348. )
  349. throws PwmUnrecoverableException, PwmOperationalException
  350. {
  351. log( PwmLogLevel.TRACE, () -> "beginning testCredentials process" );
  352. if ( userIdentity == null || userIdentity.getUserDN() == null || userIdentity.getUserDN().length() < 1 )
  353. {
  354. final String errorMsg = "attempt to authenticate with null userDN";
  355. log( PwmLogLevel.DEBUG, () -> errorMsg );
  356. throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_WRONGPASSWORD, errorMsg ) );
  357. }
  358. if ( password == null )
  359. {
  360. final String errorMsg = "attempt to authenticate with null password";
  361. log( PwmLogLevel.DEBUG, () -> errorMsg );
  362. throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_WRONGPASSWORD, errorMsg ) );
  363. }
  364. //try authenticating the user using a normal ldap BIND operation.
  365. log( PwmLogLevel.TRACE, () -> "attempting authentication using ldap BIND" );
  366. boolean bindSucceeded = false;
  367. try
  368. {
  369. //read a provider using the user's DN and password.
  370. userProvider = LdapOperationsHelper.createChaiProvider(
  371. pwmApplication,
  372. sessionLabel,
  373. userIdentity.getLdapProfile( pwmApplication.getConfig() ),
  374. pwmApplication.getConfig(),
  375. userIdentity.getUserDN(),
  376. password
  377. );
  378. //issue a read operation to trigger a bind.
  379. userProvider.readStringAttribute( userIdentity.getUserDN(), ChaiConstant.ATTR_LDAP_OBJECTCLASS );
  380. bindSucceeded = true;
  381. }
  382. catch ( ChaiException e )
  383. {
  384. if ( e.getErrorCode() != null && e.getErrorCode() == ChaiError.INTRUDER_LOCKOUT )
  385. {
  386. final String errorMsg = "intruder lockout detected for user " + userIdentity + " marking session as locked out: " + e.getMessage();
  387. final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INTRUDER_LDAP, errorMsg );
  388. log( PwmLogLevel.WARN, () -> errorInformation.toDebugStr() );
  389. throw new PwmUnrecoverableException( errorInformation );
  390. }
  391. final PwmError pwmError = PwmError.forChaiError( e.getErrorCode() );
  392. final ErrorInformation errorInformation;
  393. if ( pwmError != null && PwmError.ERROR_INTERNAL != pwmError )
  394. {
  395. errorInformation = new ErrorInformation( pwmError, e.getMessage() );
  396. }
  397. else
  398. {
  399. errorInformation = new ErrorInformation( PwmError.ERROR_WRONGPASSWORD, "ldap error during password check: " + e.getMessage() );
  400. }
  401. log( PwmLogLevel.DEBUG, () -> errorInformation.toDebugStr() );
  402. throw new PwmOperationalException( errorInformation );
  403. }
  404. finally
  405. {
  406. if ( !bindSucceeded && userProvider != null )
  407. {
  408. try
  409. {
  410. userProvider.close();
  411. userProvider = null;
  412. }
  413. catch ( Throwable e )
  414. {
  415. log( PwmLogLevel.ERROR, () -> "unexpected error closing invalid ldap connection after failed login attempt: " + e.getMessage() );
  416. }
  417. }
  418. }
  419. }
  420. private PasswordData learnUserPassword( )
  421. throws ChaiUnavailableException, PwmUnrecoverableException
  422. {
  423. log( PwmLogLevel.TRACE, () -> "beginning auth processes for user with unknown password" );
  424. return LdapOperationsHelper.readLdapPassword( pwmApplication, sessionLabel, userIdentity );
  425. }
  426. private PasswordData setTempUserPassword(
  427. )
  428. throws ChaiUnavailableException, ImpossiblePasswordPolicyException, PwmUnrecoverableException
  429. {
  430. final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN );
  431. final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider( userIdentity.getLdapProfileID() );
  432. final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser( userIdentity.getUserDN() );
  433. // try setting a random password on the account to authenticate.
  434. if ( !configAlwaysUseProxy && requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE )
  435. {
  436. log( PwmLogLevel.DEBUG, () -> "attempting to set temporary random password" );
  437. final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
  438. pwmApplication,
  439. sessionLabel,
  440. userIdentity,
  441. chaiUser,
  442. PwmConstants.DEFAULT_LOCALE
  443. );
  444. // create random password for user
  445. final RandomPasswordGenerator.RandomGeneratorConfig randomGeneratorConfig = RandomPasswordGenerator.RandomGeneratorConfig.builder()
  446. .seedlistPhrases( RandomPasswordGenerator.DEFAULT_SEED_PHRASES )
  447. .passwordPolicy( passwordPolicy )
  448. .build();
  449. final PasswordData currentPass = RandomPasswordGenerator.createRandomPassword( sessionLabel, randomGeneratorConfig, pwmApplication );
  450. try
  451. {
  452. final String oracleDSPrePasswordAllowChangeTime = oraclePreTemporaryPwHandler( chaiProvider,
  453. chaiUser );
  454. // write the random password for the user.
  455. chaiUser.setPassword( currentPass.getStringValue() );
  456. oraclePostTemporaryPwHandler( chaiProvider, chaiUser, oracleDSPrePasswordAllowChangeTime );
  457. log( PwmLogLevel.DEBUG, () -> "user " + userIdentity + " password has been set to random value to use for user authentication" );
  458. }
  459. catch ( ChaiOperationException e )
  460. {
  461. final String errorStr = "error setting random password for user " + userIdentity + " " + e.getMessage();
  462. log( PwmLogLevel.ERROR, () -> errorStr );
  463. throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr ) );
  464. }
  465. return currentPass;
  466. }
  467. return null;
  468. }
  469. private String oraclePreTemporaryPwHandler(
  470. final ChaiProvider chaiProvider,
  471. final ChaiUser chaiUser
  472. )
  473. throws PwmUnrecoverableException, ChaiUnavailableException, ChaiOperationException
  474. {
  475. if ( !pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.ORACLE_DS_ENABLE_MANIP_ALLOWCHANGETIME ) )
  476. {
  477. return null;
  478. }
  479. if ( DirectoryVendor.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor() )
  480. {
  481. return null;
  482. }
  483. // oracle DS special case: passwordAllowChangeTime handler
  484. final String oracleDSPrePasswordAllowChangeTime = chaiProvider.readStringAttribute(
  485. chaiUser.getEntryDN(),
  486. ORACLE_ATTR_PW_ALLOW_CHG_TIME );
  487. log( PwmLogLevel.TRACE, () -> "read OracleDS value of passwordAllowChangeTime value=" + oracleDSPrePasswordAllowChangeTime );
  488. if ( oracleDSPrePasswordAllowChangeTime != null && !oracleDSPrePasswordAllowChangeTime.isEmpty() )
  489. {
  490. final Instant date = OracleDSEntries.convertZuluToDate( oracleDSPrePasswordAllowChangeTime );
  491. final boolean enforceFromForgotten = !ForgottenPasswordUtil.permitPwChangeDuringMinLifetime(
  492. pwmApplication,
  493. sessionLabel,
  494. userIdentity
  495. );
  496. if ( enforceFromForgotten )
  497. {
  498. if ( Instant.now().isBefore( date ) )
  499. {
  500. final String errorMsg = "change not permitted until " + JavaHelper.toIsoDate(
  501. date );
  502. throw new PwmUnrecoverableException(
  503. new ErrorInformation( PwmError.PASSWORD_TOO_SOON, errorMsg ) );
  504. }
  505. }
  506. }
  507. return oracleDSPrePasswordAllowChangeTime;
  508. }
  509. private void oraclePostTemporaryPwHandler(
  510. final ChaiProvider chaiProvider,
  511. final ChaiUser chaiUser,
  512. final String oracleDSPrePasswordAllowChangeTime
  513. )
  514. throws ChaiUnavailableException, ChaiOperationException
  515. {
  516. if ( !pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.ORACLE_DS_ENABLE_MANIP_ALLOWCHANGETIME ) )
  517. {
  518. return;
  519. }
  520. // oracle DS special case: passwordAllowChangeTime handler
  521. if ( DirectoryVendor.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor() )
  522. {
  523. return;
  524. }
  525. if ( oracleDSPrePasswordAllowChangeTime != null && !oracleDSPrePasswordAllowChangeTime.isEmpty() )
  526. {
  527. // write back the original pre-password allow change time.
  528. final Set<String> values = new HashSet<>(
  529. Collections.singletonList( oracleDSPrePasswordAllowChangeTime ) );
  530. chaiProvider.writeStringAttribute( chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME,
  531. values,
  532. true );
  533. log( PwmLogLevel.TRACE, () -> "re-wrote passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN() + ", value=" + oracleDSPrePasswordAllowChangeTime );
  534. }
  535. else
  536. {
  537. final String oracleDSPostPasswordAllowChangeTime = chaiProvider.readStringAttribute(
  538. chaiUser.getEntryDN(),
  539. ORACLE_ATTR_PW_ALLOW_CHG_TIME );
  540. if ( oracleDSPostPasswordAllowChangeTime != null && !oracleDSPostPasswordAllowChangeTime.isEmpty() )
  541. {
  542. final boolean postTempUseCurrentTime = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.LDAP_ORACLE_POST_TEMPPW_USE_CURRENT_TIME ) );
  543. if ( postTempUseCurrentTime )
  544. {
  545. log( PwmLogLevel.TRACE, () -> "a new value for passwordAllowChangeTime attribute to user "
  546. + chaiUser.getEntryDN() + " has appeared, will replace with current time value" );
  547. final String newTimeValue = OracleDSEntries.convertDateToZulu( Instant.now() );
  548. final Set<String> values = new HashSet<>( Collections.singletonList( newTimeValue ) );
  549. chaiProvider.writeStringAttribute( chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME, values, true );
  550. log( PwmLogLevel.TRACE, () -> "wrote attribute value '" + newTimeValue + "' for passwordAllowChangeTime attribute on user "
  551. + chaiUser.getEntryDN() );
  552. }
  553. else
  554. {
  555. // password allow change time has appeared, but wasn't present previously, so delete it.
  556. log( PwmLogLevel.TRACE, () -> "a new value for passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN()
  557. + " has appeared, will remove" );
  558. chaiProvider.deleteStringAttributeValue( chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME,
  559. oracleDSPostPasswordAllowChangeTime );
  560. log( PwmLogLevel.TRACE, () -> "deleted attribute value for passwordAllowChangeTime attribute on user " + chaiUser.getEntryDN() );
  561. }
  562. }
  563. }
  564. }
  565. private boolean determineIfLdapProxyNeeded( final AuthenticationType authenticationType, final PasswordData userPassword )
  566. throws ChaiUnavailableException, PwmUnrecoverableException
  567. {
  568. if ( userProvider != null )
  569. {
  570. return false;
  571. }
  572. final boolean authIsBindInhibit = authenticationType == AuthenticationType.AUTH_BIND_INHIBIT;
  573. final boolean authIsFromForgottenPw = authenticationType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
  574. final boolean alwaysUseProxyIsEnabled = pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN );
  575. final boolean passwordNotPresent = userPassword == null;
  576. return authIsBindInhibit || authIsFromForgottenPw && ( alwaysUseProxyIsEnabled || passwordNotPresent );
  577. }
  578. private ChaiProvider makeProxyProvider( )
  579. throws ChaiUnavailableException, PwmUnrecoverableException
  580. {
  581. final LdapProfile profile = pwmApplication.getConfig().getLdapProfiles().get( userIdentity.getLdapProfileID() );
  582. final String proxyDN = profile.readSettingAsString( PwmSetting.LDAP_PROXY_USER_DN );
  583. final PasswordData proxyPassword = profile.readSettingAsPassword( PwmSetting.LDAP_PROXY_USER_PASSWORD );
  584. return LdapOperationsHelper.createChaiProvider( pwmApplication, sessionLabel, profile, pwmApplication.getConfig(), proxyDN, proxyPassword );
  585. }
  586. private void log( final PwmLogLevel level, final Supplier<CharSequence> message )
  587. {
  588. LOGGER.log( level, sessionLabel, () -> "authID=" + operationNumber + ", " + message.get() );
  589. }
  590. private String makeAuditLogMessage( final AuthenticationType authenticationType )
  591. {
  592. return "type=" + authenticationType.toString()
  593. + ", "
  594. + "source="
  595. + ( authenticationSource == null ? "null" : authenticationSource.toString() );
  596. }
  597. }