org.artifactory.security.jobs.PasswordExpireNotificationJob.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.security.jobs.PasswordExpireNotificationJob.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2013 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.security.jobs;

import org.apache.commons.lang.StringUtils;
import org.artifactory.addon.AddonsManager;
import org.artifactory.addon.CoreAddons;
import org.artifactory.api.config.CentralConfigService;
import org.artifactory.api.context.ArtifactoryContext;
import org.artifactory.api.context.ContextHelper;
import org.artifactory.api.mail.MailServerConfiguration;
import org.artifactory.api.mail.MailService;
import org.artifactory.api.security.PasswordExpiryUser;
import org.artifactory.api.security.SecurityService;
import org.artifactory.descriptor.mail.MailServerDescriptor;
import org.artifactory.schedule.JobCommand;
import org.artifactory.schedule.TaskUser;
import org.artifactory.schedule.quartz.QuartzCommand;
import org.artifactory.security.crypto.CryptoHelper;
import org.artifactory.util.HttpUtils;
import org.joda.time.DateTime;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.mail.Message;
import java.util.Set;

/**
 * A job that notifies users about expiring password
 *
 * @author Michael Pasternak
 */
@JobCommand(description = "Password expire notification job", singleton = true, runOnlyOnPrimary = true, schedulerUser = TaskUser.SYSTEM, manualUser = TaskUser.SYSTEM)
public class PasswordExpireNotificationJob extends QuartzCommand {
    private static final Logger log = LoggerFactory.getLogger(PasswordExpireNotificationJob.class);

    private static final String MAIL_SUBJECT = "Your Artifactory password is about to expire";

    private final ArtifactoryContext artifactoryContext = ContextHelper.get();

    @Override
    public void onExecute(JobExecutionContext callbackContext) throws JobExecutionException {
        log.debug("Password expire notification job is started");

        CentralConfigService configService = artifactoryContext.beanForType(CentralConfigService.class);
        if (!configService.getMutableDescriptor().getSecurity().getPasswordSettings().getExpirationPolicy()
                .isEnabled()) {
            log.debug("ExpirationPolicy is disabled");
            return;
        }
        if (!configService.getMutableDescriptor().getSecurity().getPasswordSettings().getExpirationPolicy()
                .isNotifyByEmail()) {
            log.debug("Email notification is disabled, not notifying users about expiring passwords ...");
            return;
        }

        MailService mailService = artifactoryContext.beanForType(MailService.class);
        SecurityService securityService = artifactoryContext.beanForType(SecurityService.class);

        MailServerConfiguration emailConfig = getEmailConfig();
        if (emailConfig == null || !emailConfig.isEnabled()) {
            log.debug("Mail server is not configured/disabled, not notifying users about expiring passwords ...");
            return;
        }

        Set<PasswordExpiryUser> usersWhichPasswordIsAboutToExpire = securityService
                .getUsersWhichPasswordIsAboutToExpire();
        usersWhichPasswordIsAboutToExpire.stream().filter(user -> StringUtils.isNotBlank(user.getEmail()))
                .forEach(user -> mailService.sendMail(getEmailAddresses(user.getEmail()), MAIL_SUBJECT,
                        getMailBody(user.getUserName(), user.getPasswordCreated(), false), emailConfig,
                        Message.RecipientType.TO));
        log.debug("Password expire notification job is finished");
    }

    private String[] getEmailAddresses(String addresses) {
        return new String[] { addresses };
    }

    /**
     * @return mail server configuration {@link MailServerConfiguration}
     */
    private MailServerConfiguration getEmailConfig() {
        CentralConfigService centralConfigService = artifactoryContext.beanForType(CentralConfigService.class);
        MailServerDescriptor m = centralConfigService.getMutableDescriptor().getMailServer();

        if (m == null) {
            return null;
        }

        return new MailServerConfiguration(m.isEnabled(), m.getHost(), m.getPort(), m.getUsername(),
                CryptoHelper.decryptIfNeeded(m.getPassword()), m.getFrom(), m.getSubjectPrefix(), m.isTls(),
                m.isSsl(), m.getArtifactoryUrl());
    }

    /**
     * Produces unique (per user) email body
     *
     * @param userName          user name
     * @param passwordCreated   the date when password was created
     * @param plainText         whether email should be send in plain text or html
     *
     * @return email body
     */
    public String getMailBody(String userName, Long passwordCreated, boolean plainText) {
        DateTime created = new DateTime(passwordCreated);
        CentralConfigService centralConfigService = ContextHelper.get().beanForType(CentralConfigService.class);
        int expiresIn = centralConfigService.getMutableDescriptor().getSecurity().getPasswordSettings()
                .getExpirationPolicy().getPasswordMaxAge();
        DateTime now = DateTime.now();
        int daysLeft = created.plusDays(expiresIn).minusDays(now.getDayOfYear()).getDayOfYear();
        if ((daysLeft == 365 || daysLeft == 366) && created.plusDays(expiresIn).dayOfYear().get() != daysLeft)
            daysLeft = 0;
        DateTime dueDate = now.plusDays(daysLeft);

        if (daysLeft == 0) {
            if (!plainText)
                return String.format(
                        "<!DOCTYPE html>" + "<html>" + "<body>" + "<p>" + "Dear %s,<br><br>"
                                + "Your Artifactory password is about to expire today (%s).<br>"
                                + "Please change your password before it expires, "
                                + "Once expired you will not be able to  access the<br>"
                                + "Artifactory UI or REST API using that password until it will be changed.<br><br>"
                                + "Password can be changed in <a href=\"" + getProfilePageUrl()
                                + "\">your profile page</a> " + "or by your system administrator.<br><br>" + "</p>"
                                + "</body>" + "</html>",
                        userName.toUpperCase(), dueDate.toString("MMMMM dd, YYYY"));
            return String.format("Dear %s,\n" + "\n" + "Your Artifactory password is about to expire today (%s).\n"
                    + "Please change your password before it expires. Once expired you will not be able to  access the\n"
                    + "Artifactory UI or REST API using that password until it will be changed.\n" + "\n"
                    + "Password can be changed in your page [1] or by your system administrator.\n\n" + "[1] "
                    + getProfilePageUrl(), userName.toUpperCase(), dueDate.toString("MMMMM dd, YYYY"));
        } else {
            if (!plainText)
                return String.format("<!DOCTYPE html>" + "<html>" + "<body>" + "<p>" + "Dear %s,<br><br>"
                        + "Your Artifactory password is about to expire in %d " + (daysLeft == 1 ? "day" : "days")
                        + " (%s).<br>" + "Please change your password before it expires, "
                        + "Once expired you will not be able to  access the<br>"
                        + "Artifactory UI or REST API using that password until it will be changed.<br><br>"
                        + "Password can be changed in <a href=\"" + getProfilePageUrl()
                        + "\">your profile page</a> " + "or by your system administrator.<br><br>" + "</p>"
                        + "</body>" + "</html>", userName.toUpperCase(), daysLeft,
                        dueDate.toString("MMMMM dd, YYYY"));
            return String.format("Dear %s,\n" + "\n" + "Your Artifactory password is about to expire in %d "
                    + (daysLeft == 1 ? "day" : "days") + " (%s).\n"
                    + "Please change your password before it expires. Once expired you will not be able to  access the\n"
                    + "Artifactory UI or REST API using that password until it will be changed.\n" + "\n"
                    + "Password can be changed in your page [1] or by your system administrator.\n\n" + "[1] "
                    + getProfilePageUrl(), userName.toUpperCase(), daysLeft, dueDate.toString("MMMMM dd, YYYY"));
        }
    }

    /**
     * @return a URL referring artifactory instance profile page
     */
    private String getProfilePageUrl() {
        CoreAddons addon = ContextHelper.get().beanForType(AddonsManager.class).addonByType(CoreAddons.class);
        String resetPageUrl = addon.getArtifactoryUrl();
        if (resetPageUrl != null && StringUtils.isNotBlank(resetPageUrl)) {
            if (!resetPageUrl.endsWith("/")) {
                resetPageUrl += "/";
            }
            if (addon.isAol()) {
                resetPageUrl += "#/profile";
            } else {
                resetPageUrl += HttpUtils.WEBAPP_URL_PATH_PREFIX + "/#/profile ";
            }
        }
        return resetPageUrl;
    }
}