// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package com.netscape.cms.jobs;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.StringTokenizer;
import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.IConfigStore;
import com.netscape.certsrv.base.IExtendedPluginInfo;
import com.netscape.certsrv.base.ISubsystem;
import com.netscape.certsrv.base.MetaInfo;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.dbs.IElementProcessor;
import com.netscape.certsrv.dbs.certdb.ICertRecord;
import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
import com.netscape.certsrv.jobs.IJob;
import com.netscape.certsrv.jobs.IJobCron;
import com.netscape.certsrv.jobs.IJobsScheduler;
import com.netscape.certsrv.logging.ILogger;
import com.netscape.certsrv.notification.ENotificationException;
import com.netscape.certsrv.notification.IEmailFormProcessor;
import com.netscape.certsrv.notification.IEmailResolver;
import com.netscape.certsrv.notification.IEmailResolverKeys;
import com.netscape.certsrv.notification.IMailNotification;
import com.netscape.certsrv.request.IRequest;
import com.netscape.certsrv.request.RequestId;
/**
* A job for the Jobs Scheduler. This job checks in the internal ldap
* db for certs about to expire within the next configurable days and
* sends email notifications to the appropriate recipients.
*
* the $TOKENS that are available for the this jobs's summary outer form are:
*
*
$Status
*
$InstanceID
*
$SummaryItemList
*
$SummaryTotalNum
*
$SummaryTotalSuccess
*
$SummaryTotalfailure
*
$ExecutionTime
*
* and for the inner list items:
*
*
$SerialNumber
*
$IssuerDN
*
$SubjectDN
*
$NotAfter
*
$NotBefore
*
$RequestorEmail
*
$CertType
*
$RequestType
*
$HttpHost
*
$HttpPort
*
*
* @version $Revision$, $Date$
* @see com.netscape.certsrv.jobs.IJob
* @see com.netscape.cms.jobs.AJobBase
*/
public class RenewalNotificationJob
extends AJobBase
implements IJob, Runnable, IExtendedPluginInfo {
// config parameters...
public static final String PROP_CRON = "cron";
/**
* Profile ID specifies which profile approves the certificate.
*/
public static final String PROP_PROFILE_ID = "profileId";
/**
* This job will send notification at this much time before the
* enpiration date
*/
public static final String PROP_NOTIFYTRIGGEROFFSET =
"notifyTriggerOffset";
/**
* This job will stop sending notification this much time after
* the expiration date
*/
public static final String PROP_NOTIFYENDOFFSET = "notifyEndOffset";
/**
* sender email address as appeared on the notification email
*/
public static final String PROP_SENDEREMAIL =
"senderEmail";
/**
* email subject line as appeared on the notification email
*/
public static final String PROP_EMAILSUBJECT =
"emailSubject";
/**
* location of the template file used for email notification
*/
public static final String PROP_EMAILTEMPLATE = "emailTemplate";
public static final String PROP_MAXNOTIFYCOUNT = "maxNotifyCount";
/**
* sender email as appeared on the notification summary email
*/
public static final String PROP_SUMMARY_SENDEREMAIL = "summary.senderEmail";
/**
* recipient of the notification summary email
*/
public static final String PROP_SUMMARY_RECIPIENTEMAIL = "summary.recipientEmail";
/**
* email subject as appeared on the notification summary email
*/
public static final String PROP_SUMMARY_SUBJECT = "summary.emailSubject";
/**
* location of the email template used for notification summary
*/
public static final String PROP_SUMMARY_TEMPLATE = "summary.emailTemplate";
/**
* location of the template file for each item appeared on the
* notification summary
*/
public static final String PROP_SUMMARY_ITEMTEMPLATE = "summary.itemTemplate";
/*
* Holds configuration parameters accepted by this implementation.
* This list is passed to the configuration console so configuration
* for instances of this implementation can be configured through the
* console.
*/
protected static String[] mConfigParams =
new String[] {
"enabled",
PROP_CRON,
PROP_PROFILE_ID,
PROP_NOTIFYTRIGGEROFFSET,
PROP_NOTIFYENDOFFSET,
PROP_SENDEREMAIL,
PROP_EMAILSUBJECT,
PROP_EMAILTEMPLATE,
"summary.enabled",
PROP_SUMMARY_RECIPIENTEMAIL,
PROP_SUMMARY_SENDEREMAIL,
PROP_SUMMARY_SUBJECT,
PROP_SUMMARY_ITEMTEMPLATE,
PROP_SUMMARY_TEMPLATE,
};
protected ICertificateRepository mCertDB = null;
protected ICertificateAuthority mCA = null;
protected boolean mSummary = false;
protected String mEmailSender = null;
protected String mEmailSubject = null;
protected String mEmailTemplateName = null;
protected String mSummaryItemTemplateName = null;
protected String mSummaryTemplateName = null;
protected boolean mSummaryHTML = false;
protected boolean mHTML = false;
protected String mHttpHost = null;
protected String mHttpPort = null;
private int mPreDays = 0;
private long mPreMS = 0;
private int mPostDays = 0;
private long mPostMS = 0;
private String[] mProfileId = null;
/**
* class constructor
*/
public RenewalNotificationJob() {
}
/**
* holds help text for this plugin
*/
public String[] getExtendedPluginInfo(Locale locale) {
String s[] = {
IExtendedPluginInfo.HELP_TEXT +
"; A job that checks for expiring or expired certs" +
"notifyTriggerOffset before and notifyEndOffset after " +
"the expiration date",
PROP_PROFILE_ID + ";string;Specify the ID of the profile which " +
"approved the certificates that are about to expire. For multiple " +
"profiles, each entry is separated by white space. For example, " +
"if the administrator just wants to give automated notification " +
"when the SSL server certificates are about to expire, then " +
"he should enter \"caServerCert caAgentServerCert\" in the profileId textfield. " +
"Blank field means all profiles.",
PROP_NOTIFYTRIGGEROFFSET + ";number,required;How long (in days) before " +
"certificate expiration will the first notification " +
"be sent",
PROP_NOTIFYENDOFFSET + ";number,required;How long (in days) after " +
"certificate expiration will notifications " +
"continue to be resent if certificate is not renewed",
PROP_CRON + ";string,required;Format: minute hour dayOfMonth Mmonth " +
"dayOfWeek. Use '*' for 'every'. For dayOfWeek, 0 is Sunday",
PROP_SENDEREMAIL + ";string,required;Specify the address to be used " +
"as the email's 'sender'. Bounces go to this address.",
PROP_EMAILSUBJECT + ";string,required;Email subject",
PROP_EMAILTEMPLATE + ";string,required;Fully qualified pathname of " +
"template file of email to be sent",
"enabled;boolean;Enable this plugin",
"summary.enabled;boolean;Enabled sending of summaries",
PROP_SUMMARY_SENDEREMAIL + ";string,required;Sender email address of summary",
PROP_SUMMARY_RECIPIENTEMAIL + ";string,required;Who should receive summaries",
PROP_SUMMARY_SUBJECT + ";string,required;Subject of summary email",
PROP_SUMMARY_TEMPLATE + ";string,required;Fully qualified pathname of " +
"template file of email to be sent",
PROP_SUMMARY_ITEMTEMPLATE + ";string,required;Fully qualified pathname of " +
"file with template to be used for each summary item",
IExtendedPluginInfo.HELP_TOKEN +
";configuration-jobrules-renewalnotification",
};
return s;
}
/**
* Initialize from the configuration file.
*
* @param id String name of this instance
* @param implName string name of this implementation
* @param config configuration store for this instance
* @exception EBaseException
*/
public void init(ISubsystem owner, String id, String implName, IConfigStore config) throws
EBaseException {
mConfig = config;
mId = id;
mImplName = implName;
mCA = (ICertificateAuthority)
CMS.getSubsystem("ca");
if (mCA == null) {
mSummary = false;
return;
}
mCertDB = mCA.getCertificateRepository();
mCron = mConfig.getString(IJobCron.PROP_CRON);
if (mCron == null) {
return;
}
// parse cron string into a JobCron class
IJobsScheduler scheduler = (IJobsScheduler) owner;
mJobCron = scheduler.createJobCron(mCron);
}
/**
* finds out which cert needs notification and notifies the
* responsible parties
*/
public void run() {
// for forming renewal URL at template
mHttpHost = CMS.getEEHost();
mHttpPort = CMS.getEESSLPort();
// read from the configuration file
try {
mPreDays = mConfig.getInteger(PROP_NOTIFYTRIGGEROFFSET, 30); // in days
mPostDays = mConfig.getInteger(PROP_NOTIFYENDOFFSET, 15); // in days
mEmailSender = mConfig.getString(PROP_SENDEREMAIL);
mEmailSubject = mConfig.getString(PROP_EMAILSUBJECT);
mEmailTemplateName = mConfig.getString(PROP_EMAILTEMPLATE);
// initialize the summary related config info
IConfigStore sc = mConfig.getSubStore(PROP_SUMMARY);
if (sc.getBoolean(PROP_ENABLED, false)) {
mSummary = true;
mSummaryItemTemplateName =
mConfig.getString(PROP_SUMMARY_ITEMTEMPLATE);
mSummarySenderEmail =
mConfig.getString(PROP_SUMMARY_SENDEREMAIL);
mSummaryReceiverEmail =
mConfig.getString(PROP_SUMMARY_RECIPIENTEMAIL);
mSummaryMailSubject =
mConfig.getString(PROP_SUMMARY_SUBJECT);
mSummaryTemplateName =
mConfig.getString(PROP_SUMMARY_TEMPLATE);
} else {
mSummary = false;
}
long msperday = 86400 * 1000;
long mspredays = mPreDays;
long mspostdays = mPostDays;
mPreMS = mspredays * msperday;
mPostMS = mspostdays * msperday;
Date now = CMS.getCurrentDate();
DateFormat dateFormat = DateFormat.getDateTimeInstance();
String nowString = dateFormat.format(now);
/*
* look in the internal db for certificateRecords that are
* 1. within the expiration notification period
* 2. has not yet been renewed
* 3. notify - use EmailTemplateProcessor to formulate
* content, then send
* if notified successfully, mark "STATUS_SUCCESS",
* else, if notified unsuccessfully, mark "STATUS_FAILURE".
*/
/* 1) make target notAfter string */
Date expiryDate = null;
Date stopDate = null;
/* 2) Assemble ldap Search filter string */
// date format: 19991215125306Z
long expiryMS = now.getTime() + mPreMS;
long stopMS = now.getTime() - mPostMS;
expiryDate = new Date(expiryMS);
stopDate = new Date(stopMS);
// All cert records which:
// 1) expire before the deadline
// 2) have not already been renewed
// filter format:
// (& (notafter<='time')(!(certAutoRenew=DONE))(!certAutoRenew=DISABLED))
StringBuffer f = new StringBuffer();
String profileId = "";
try {
profileId = mConfig.getString(PROP_PROFILE_ID, "");
} catch (EBaseException ee) {
}
if (profileId != null && profileId.length() > 0) {
StringTokenizer tokenizer = new StringTokenizer(profileId);
int num = tokenizer.countTokens();
mProfileId = new String[num];
for (int i = 0; i < num; i++)
mProfileId[i] = tokenizer.nextToken();
}
f.append("(&");
if (mProfileId != null) {
if (mProfileId.length == 1)
f.append("(" + ICertRecord.ATTR_META_INFO + "=" +
ICertRecord.META_PROFILE_ID + ":" + mProfileId[0] + ")");
else {
f.append("(|");
for (int i = 0; i < mProfileId.length; i++) {
f.append("(" + ICertRecord.ATTR_META_INFO + "=" +
ICertRecord.META_PROFILE_ID + ":" + mProfileId[i] + ")");
}
f.append(")");
}
}
f.append("(" + ICertRecord.ATTR_X509CERT + ".notAfter" + "<=" + expiryDate.getTime() + ")");
f.append("(" + ICertRecord.ATTR_X509CERT + ".notAfter" + ">=" + stopDate.getTime() + ")");
f.append("(!(" + ICertRecord.ATTR_AUTO_RENEW + "=" + ICertRecord.AUTO_RENEWAL_DONE + "))");
f.append("(!(" + ICertRecord.ATTR_AUTO_RENEW + "=" + ICertRecord.AUTO_RENEWAL_DISABLED + "))");
f.append("(!(" + ICertRecord.ATTR_CERT_STATUS + "=" + ICertRecord.STATUS_REVOKED + "))");
f.append("(!(" + ICertRecord.ATTR_CERT_STATUS + "=" + ICertRecord.STATUS_REVOKED_EXPIRED + "))");
f.append(")");
String filter = f.toString();
String emailTemplate =
getTemplateContent(mEmailTemplateName);
mHTML = mMailHTML;
try {
String summaryItemTemplate = null;
if (mSummary == true) {
summaryItemTemplate =
getTemplateContent(mSummaryItemTemplateName);
}
ItemCounter ic = new ItemCounter();
CertRecProcessor cp = new CertRecProcessor(this, emailTemplate, summaryItemTemplate, ic);
//CertRecordList list = mCertDB.findCertRecordsInList(filter, null, "serialno", 5);
//list.processCertRecords(0, list.getSize() - 1, cp);
Enumeration