/******************************************************************************* * Copyright (c) 2013, 2014 Lectorius, Inc. * Authors: * Vijay Pandurangan (vijayp@mitro.co) * Evan Jones (ej@mitro.co) * Adam Hilss (ahilss@mitro.co) * * * 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, either version 3 of the License, or * (at your option) any later version. * * 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, see <http://www.gnu.org/licenses/>. * * You can contact the authors at inbound@mitro.co. *******************************************************************************/ package co.mitro.core.alerts; import java.sql.SQLException; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import co.mitro.analysis.AuditLogProcessor.ActionType; import co.mitro.core.server.Manager; import co.mitro.core.server.data.DBFutureAlert; import co.mitro.core.server.data.DBFutureAlert.AlertPredicate; import co.mitro.core.server.data.DBProcessedAudit; import com.google.common.base.Joiner; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.j256.ormlite.stmt.QueryBuilder; /** * This sends out e-mails whenever secrets are shared with a user. * It currently aggregates all secrets shared with a particular user * (Not yet TODO! It cancels shares when access to a particular secret is revoked, * or when the user has logged into the secret) * */ public class SecretsSharedWithUserEmailer extends BaseEmailAlerter { private static final Logger logger = LoggerFactory.getLogger(SecretsSharedWithUserEmailer.class); private static final long MIN_TIME_AFTER_SHARE_MS= TimeUnit.MINUTES.toMillis(2); private static final Set<ActionType> IMPORTANT_ACTIONS = Sets.immutableEnumSet(ActionType.GRANTED_ACCESS_TO); private static final Joiner JOINER = Joiner.on(", "); @Override public int createNewDBFutureAlertsForProcessedAudits( Manager mgr, Collection<DBProcessedAudit> audits, long minTimestampMs) throws SQLException { int count = 0; for (DBProcessedAudit action : audits) { if (action.getAction() == ActionType.GRANTED_ACCESS_TO) { // TODO: making these unique per target here will be more efficient // but it's done in addnew anyway DBFutureAlert.addNew(mgr, AlertPredicate.EMAIL_TARGET_USER_ON_SHARES, action, action.getAffectedUser(), minTimestampMs); ++count; } } return count; } @Override protected AlertPredicate getMatchingAlertType() { return AlertPredicate.EMAIL_TARGET_USER_ON_SHARES; } @Override protected boolean internalProcess(AlerterContext ac) throws SQLException { // if we haven't wanted at least min_time, fail. ac.dbAlertObject.nextCheckTimestampMs = Math.max(ac.dbAlertObject.nextCheckTimestampMs, ac.dbAlertObject.enqueueTimestampMs + MIN_TIME_AFTER_SHARE_MS); if (System.currentTimeMillis() < ac.dbAlertObject.nextCheckTimestampMs) { return false; } // get all the grant actions for this user QueryBuilder<DBProcessedAudit,Integer> builder = getBuilder(ac, IMPORTANT_ACTIONS, AlertUserType.OBJECT); // this orders them descending by timestamp. builder.orderBy(DBProcessedAudit.TIMESTAMP_FIELD_NAME, false); List<DBProcessedAudit> matchingAudits = ac.manager.processedAuditDao.query(builder.prepare()); assert (!matchingAudits.isEmpty()) : "this cannot be!"; // first element is most recent event. if (matchingAudits.get(0).getTimestampMs() + MIN_TIME_AFTER_SHARE_MS > System.currentTimeMillis()) { // not enough time has transpired. ac.dbAlertObject.nextCheckTimestampMs = matchingAudits.get(0).getTimestampMs() + MIN_TIME_AFTER_SHARE_MS; return false; } Set<String> sharingUsers = Sets.newHashSet(); int numSharedSecrets = 0; for (DBProcessedAudit audit : matchingAudits) { sharingUsers.add(audit.getActorName()); if (null != audit.getAffectedSecret()) { numSharedSecrets += 1; } } // TODO: query the DB for processed audit logs showing that the user actually // logged into that secret, then cancel email for those secrets. // no such actions have happened. we should trigger the alert. Map<String, String> emailParams = Maps.newHashMap(); emailParams.put("fullname", JOINER.join(sharingUsers)); // TODO: fix the user name emailParams.put("username", "UNKNOWN"); emailParams.put("numsecrets", Integer.toString(numSharedSecrets)); // TODO: this should probably point to the specific secret or something? emailParams.put("secreturl", "https://www.mitro.co"); logger.info("ignoring share email about ", emailParams.get("fullname")); //enqueueEmail(ac, emailParams, DBEmailQueue.Type.MANDRILL_SHARE_SECRET); return true; } }