|
@@ -31,12 +31,18 @@ import password.pwm.bean.EmailItemBean;
|
|
import password.pwm.bean.SessionLabel;
|
|
import password.pwm.bean.SessionLabel;
|
|
import password.pwm.bean.UserIdentity;
|
|
import password.pwm.bean.UserIdentity;
|
|
import password.pwm.config.PwmSetting;
|
|
import password.pwm.config.PwmSetting;
|
|
|
|
+import password.pwm.config.value.data.UserPermission;
|
|
import password.pwm.error.PwmError;
|
|
import password.pwm.error.PwmError;
|
|
import password.pwm.error.PwmOperationalException;
|
|
import password.pwm.error.PwmOperationalException;
|
|
import password.pwm.error.PwmUnrecoverableException;
|
|
import password.pwm.error.PwmUnrecoverableException;
|
|
import password.pwm.ldap.LdapOperationsHelper;
|
|
import password.pwm.ldap.LdapOperationsHelper;
|
|
|
|
+import password.pwm.ldap.LdapPermissionTester;
|
|
import password.pwm.ldap.UserInfo;
|
|
import password.pwm.ldap.UserInfo;
|
|
import password.pwm.ldap.UserInfoFactory;
|
|
import password.pwm.ldap.UserInfoFactory;
|
|
|
|
+import password.pwm.svc.stats.EventRateMeter;
|
|
|
|
+import password.pwm.svc.stats.Statistic;
|
|
|
|
+import password.pwm.svc.stats.StatisticsManager;
|
|
|
|
+import password.pwm.util.java.ConditionalTaskExecutor;
|
|
import password.pwm.util.java.JavaHelper;
|
|
import password.pwm.util.java.JavaHelper;
|
|
import password.pwm.util.java.TimeDuration;
|
|
import password.pwm.util.java.TimeDuration;
|
|
import password.pwm.util.logging.PwmLogger;
|
|
import password.pwm.util.logging.PwmLogger;
|
|
@@ -44,29 +50,43 @@ import password.pwm.util.macro.MacroMachine;
|
|
|
|
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.io.Writer;
|
|
import java.io.Writer;
|
|
|
|
+import java.math.BigDecimal;
|
|
import java.time.Duration;
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.time.Instant;
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.time.temporal.ChronoUnit;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Collection;
|
|
import java.util.Iterator;
|
|
import java.util.Iterator;
|
|
|
|
+import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Locale;
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
public class PwNotifyEngine
|
|
public class PwNotifyEngine
|
|
{
|
|
{
|
|
-
|
|
|
|
private static final PwmLogger LOGGER = PwmLogger.forClass( PwNotifyEngine.class );
|
|
private static final PwmLogger LOGGER = PwmLogger.forClass( PwNotifyEngine.class );
|
|
|
|
|
|
private static final SessionLabel SESSION_LABEL = SessionLabel.PW_EXP_NOTICE_LABEL;
|
|
private static final SessionLabel SESSION_LABEL = SessionLabel.PW_EXP_NOTICE_LABEL;
|
|
|
|
|
|
-
|
|
|
|
private final PwNotifySettings settings;
|
|
private final PwNotifySettings settings;
|
|
private final PwmApplication pwmApplication;
|
|
private final PwmApplication pwmApplication;
|
|
private final Writer debugWriter;
|
|
private final Writer debugWriter;
|
|
private final StringBuffer internalLog = new StringBuffer( );
|
|
private final StringBuffer internalLog = new StringBuffer( );
|
|
|
|
+ private final List<UserPermission> permissionList;
|
|
|
|
|
|
- private volatile boolean running;
|
|
|
|
|
|
+ private final ConditionalTaskExecutor debugOutputTask = new ConditionalTaskExecutor(
|
|
|
|
+ this::periodicDebugOutput,
|
|
|
|
+ new ConditionalTaskExecutor.TimeDurationPredicate( 1, TimeUnit.MINUTES )
|
|
|
|
+ );
|
|
|
|
|
|
|
|
+ private EventRateMeter eventRateMeter = new EventRateMeter( new TimeDuration( 5, TimeUnit.MINUTES ) );
|
|
|
|
+
|
|
|
|
+ private int examinedCount = 0;
|
|
|
|
+ private int noticeCount = 0;
|
|
|
|
+ private Instant startTime;
|
|
|
|
+
|
|
|
|
+ private volatile boolean running;
|
|
|
|
|
|
- public PwNotifyEngine(
|
|
|
|
|
|
+ PwNotifyEngine(
|
|
final PwmApplication pwmApplication,
|
|
final PwmApplication pwmApplication,
|
|
final Writer debugWriter
|
|
final Writer debugWriter
|
|
)
|
|
)
|
|
@@ -74,6 +94,7 @@ public class PwNotifyEngine
|
|
this.pwmApplication = pwmApplication;
|
|
this.pwmApplication = pwmApplication;
|
|
this.settings = PwNotifySettings.fromConfiguration( pwmApplication.getConfig() );
|
|
this.settings = PwNotifySettings.fromConfiguration( pwmApplication.getConfig() );
|
|
this.debugWriter = debugWriter;
|
|
this.debugWriter = debugWriter;
|
|
|
|
+ this.permissionList = pwmApplication.getConfig().readSettingAsUserPermission( PwmSetting.PW_EXPY_NOTIFY_PERMISSION );
|
|
}
|
|
}
|
|
|
|
|
|
public boolean isRunning()
|
|
public boolean isRunning()
|
|
@@ -86,27 +107,49 @@ public class PwNotifyEngine
|
|
return internalLog.toString();
|
|
return internalLog.toString();
|
|
}
|
|
}
|
|
|
|
|
|
- private void checkIfRunningOnMaster( final String msg ) throws PwmUnrecoverableException
|
|
|
|
|
|
+ private boolean checkIfRunningOnMaster( )
|
|
{
|
|
{
|
|
if ( !pwmApplication.getPwmEnvironment().isInternalRuntimeInstance() )
|
|
if ( !pwmApplication.getPwmEnvironment().isInternalRuntimeInstance() )
|
|
{
|
|
{
|
|
- if ( pwmApplication.getClusterService() != null && !pwmApplication.getClusterService().isMaster() )
|
|
|
|
|
|
+ if ( pwmApplication.getClusterService() != null && pwmApplication.getClusterService().isMaster() )
|
|
{
|
|
{
|
|
- throw PwmUnrecoverableException.newException( PwmError.ERROR_SERVICE_NOT_AVAILABLE, msg );
|
|
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
|
|
|
|
- public void executeJob( )
|
|
|
|
|
|
+ boolean canRunOnThisServer()
|
|
|
|
+ {
|
|
|
|
+ return checkIfRunningOnMaster();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void executeJob( )
|
|
throws ChaiUnavailableException, ChaiOperationException, PwmOperationalException, PwmUnrecoverableException
|
|
throws ChaiUnavailableException, ChaiOperationException, PwmOperationalException, PwmUnrecoverableException
|
|
{
|
|
{
|
|
|
|
+ startTime = Instant.now();
|
|
|
|
+ examinedCount = 0;
|
|
|
|
+ noticeCount = 0;
|
|
try
|
|
try
|
|
{
|
|
{
|
|
- checkIfRunningOnMaster( "job can run only on a server that is currently the cluster master" );
|
|
|
|
|
|
+ internalLog.delete( 0, internalLog.length() );
|
|
running = true;
|
|
running = true;
|
|
|
|
|
|
- final Instant startTime = Instant.now();
|
|
|
|
- internalLog.delete( 0, internalLog.length() );
|
|
|
|
|
|
+ if ( !canRunOnThisServer() )
|
|
|
|
+ {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ( JavaHelper.isEmpty( permissionList ) )
|
|
|
|
+ {
|
|
|
|
+ log( "no users are included in permission list setting "
|
|
|
|
+ + PwmSetting.PW_EXPY_NOTIFY_PERMISSION.toMenuLocationDebug( null, null )
|
|
|
|
+ + ", exiting."
|
|
|
|
+ );
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
log( "starting job, beginning ldap search" );
|
|
log( "starting job, beginning ldap search" );
|
|
final Iterator<UserIdentity> workQueue = LdapOperationsHelper.readAllUsersFromLdap(
|
|
final Iterator<UserIdentity> workQueue = LdapOperationsHelper.readAllUsersFromLdap(
|
|
pwmApplication,
|
|
pwmApplication,
|
|
@@ -114,20 +157,43 @@ public class PwNotifyEngine
|
|
null,
|
|
null,
|
|
settings.getMaxLdapSearchSize()
|
|
settings.getMaxLdapSearchSize()
|
|
);
|
|
);
|
|
|
|
+
|
|
log( "ldap search complete, examining users..." );
|
|
log( "ldap search complete, examining users..." );
|
|
- int examinedCount = 0;
|
|
|
|
- int noticeCount = 0;
|
|
|
|
while ( workQueue.hasNext() )
|
|
while ( workQueue.hasNext() )
|
|
{
|
|
{
|
|
- checkIfRunningOnMaster( "job interrupted, server is no longer the cluster master." );
|
|
|
|
|
|
+ if ( !checkIfRunningOnMaster() )
|
|
|
|
+ {
|
|
|
|
+ final String msg = "job interrupted, server is no longer the cluster master.";
|
|
|
|
+ log( msg );
|
|
|
|
+ throw PwmUnrecoverableException.newException( PwmError.ERROR_SERVICE_NOT_AVAILABLE, msg );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ checkIfRunningOnMaster( );
|
|
examinedCount++;
|
|
examinedCount++;
|
|
- final UserIdentity userIdentity = workQueue.next();
|
|
|
|
- if ( processUserIdentity( userIdentity ) )
|
|
|
|
|
|
+
|
|
|
|
+ final List<UserIdentity> batch = new ArrayList<>( );
|
|
|
|
+ final int batchSize = settings.getBatchCount();
|
|
|
|
+
|
|
|
|
+ while ( batch.size() < batchSize && workQueue.hasNext() )
|
|
{
|
|
{
|
|
- noticeCount++;
|
|
|
|
|
|
+ batch.add( workQueue.next() );
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ final Instant startBatch = Instant.now();
|
|
|
|
+ examinedCount += batch.size();
|
|
|
|
+ noticeCount += processBatch( batch );
|
|
|
|
+ eventRateMeter.markEvents( batchSize );
|
|
|
|
+ final TimeDuration batchTime = TimeDuration.fromCurrent( startBatch );
|
|
|
|
+ final TimeDuration pauseTime = new TimeDuration(
|
|
|
|
+ settings.getBatchTimeMultiplier().multiply( new BigDecimal( batchTime.getTotalMilliseconds() ) ).longValue(),
|
|
|
|
+ TimeUnit.MILLISECONDS );
|
|
|
|
+ pauseTime.pause();
|
|
|
|
+
|
|
|
|
+ debugOutputTask.conditionallyExecuteTask();
|
|
}
|
|
}
|
|
- log( "job complete, " + examinedCount + " users evaluated in " + TimeDuration.fromCurrent( startTime ).asCompactString() );
|
|
|
|
|
|
+ log( "job complete, " + examinedCount + " users evaluated in " + TimeDuration.fromCurrent( startTime ).asCompactString()
|
|
|
|
+ + ", sent " + noticeCount + " notices."
|
|
|
|
+ );
|
|
}
|
|
}
|
|
finally
|
|
finally
|
|
{
|
|
{
|
|
@@ -135,11 +201,37 @@ public class PwNotifyEngine
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private void periodicDebugOutput()
|
|
|
|
+ {
|
|
|
|
+ log( "job in progress, " + examinedCount + " users evaluated in " + TimeDuration.fromCurrent( startTime ).asCompactString()
|
|
|
|
+ + ", sent " + noticeCount + " notices."
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private int processBatch( final Collection<UserIdentity> batch )
|
|
|
|
+ throws PwmUnrecoverableException
|
|
|
|
+ {
|
|
|
|
+ int count = 0;
|
|
|
|
+ for ( final UserIdentity userIdentity : batch )
|
|
|
|
+ {
|
|
|
|
+ if ( processUserIdentity( userIdentity ) )
|
|
|
|
+ {
|
|
|
|
+ count++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return count;
|
|
|
|
+ }
|
|
|
|
+
|
|
private boolean processUserIdentity(
|
|
private boolean processUserIdentity(
|
|
final UserIdentity userIdentity
|
|
final UserIdentity userIdentity
|
|
)
|
|
)
|
|
throws PwmUnrecoverableException
|
|
throws PwmUnrecoverableException
|
|
{
|
|
{
|
|
|
|
+ if ( !LdapPermissionTester.testUserPermissions( pwmApplication, SessionLabel.SYSTEM_LABEL, userIdentity, permissionList ) )
|
|
|
|
+ {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
final ChaiUser theUser = pwmApplication.getProxiedChaiUser( userIdentity );
|
|
final ChaiUser theUser = pwmApplication.getProxiedChaiUser( userIdentity );
|
|
final Instant passwordExpirationTime = LdapOperationsHelper.readPasswordExpirationTime( theUser );
|
|
final Instant passwordExpirationTime = LdapOperationsHelper.readPasswordExpirationTime( theUser );
|
|
|
|
|
|
@@ -222,7 +314,7 @@ public class PwNotifyEngine
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- void sendNoticeEmail( final UserIdentity userIdentity )
|
|
|
|
|
|
+ private void sendNoticeEmail( final UserIdentity userIdentity )
|
|
throws PwmUnrecoverableException
|
|
throws PwmUnrecoverableException
|
|
{
|
|
{
|
|
final Locale userLocale = PwmConstants.DEFAULT_LOCALE;
|
|
final Locale userLocale = PwmConstants.DEFAULT_LOCALE;
|
|
@@ -237,6 +329,7 @@ public class PwNotifyEngine
|
|
userIdentity, userLocale
|
|
userIdentity, userLocale
|
|
);
|
|
);
|
|
|
|
|
|
|
|
+ StatisticsManager.incrementStat( pwmApplication, Statistic.PWNOTIFY_EMAILS_SENT );
|
|
pwmApplication.getEmailQueue().submitEmail( emailItemBean, userInfoBean, macroMachine );
|
|
pwmApplication.getEmailQueue().submitEmail( emailItemBean, userInfoBean, macroMachine );
|
|
}
|
|
}
|
|
|
|
|
|
@@ -256,7 +349,7 @@ public class PwNotifyEngine
|
|
}
|
|
}
|
|
catch ( IOException e )
|
|
catch ( IOException e )
|
|
{
|
|
{
|
|
- LOGGER.warn( "unexpected IO error writing to debugWriter: " + e.getMessage() );
|
|
|
|
|
|
+ LOGGER.warn( SessionLabel.PWNOTIFY_SESSION_LABEL, "unexpected IO error writing to debugWriter: " + e.getMessage() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -274,6 +367,6 @@ public class PwNotifyEngine
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- LOGGER.trace( output );
|
|
|
|
|
|
+ LOGGER.trace( SessionLabel.PWNOTIFY_SESSION_LABEL, output );
|
|
}
|
|
}
|
|
}
|
|
}
|