org.usergrid.management.EmailFlowIT.java Source code

Java tutorial

Introduction

Here is the source code for org.usergrid.management.EmailFlowIT.java

Source

/*******************************************************************************
 * Copyright 2012 Apigee Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.usergrid.management;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.jvnet.mock_javamail.Mailbox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usergrid.ServiceITSetup;
import org.usergrid.ServiceITSetupImpl;
import org.usergrid.cassandra.CassandraResource;
import org.usergrid.cassandra.ClearShiroSubject;
import org.usergrid.management.cassandra.ManagementServiceImpl;
import org.usergrid.persistence.EntityManager;
import org.usergrid.persistence.SimpleEntityRef;
import org.usergrid.persistence.cassandra.CassandraService;
import org.usergrid.persistence.entities.Application;
import org.usergrid.persistence.entities.User;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrSubstitutor;

import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_PASSWORD_RESET;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_USER_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_PASSWORD_RESET;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_PIN_REQUEST;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_NOTIFY_ADMIN_OF_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_ORGANIZATIONS_REQUIRE_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_APPROVES_ADMIN_USERS;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_APPROVES_ORGANIZATIONS;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_EMAIL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_USER_ACTIVATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_USER_CONFIRMATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_USER_RESETPW_URL;

/**
 * This test cannot be run concurrently because it changes the management service properties that control how the
 * activation workflow is handled and it uses a shared global mock Mailbox to process emails.
 * <p/>
 * Hence there can be race conditions between test methods in this class.
 */
public class EmailFlowIT {
    private static final Logger LOG = LoggerFactory.getLogger(EmailFlowIT.class);
    private static final String ORGANIZATION_NAME = "email-test-org-1";
    public static final String ORGANIZATION_NAME_2 = "email-test-org-2";

    static CassandraResource cassandraResource = CassandraResource.newWithAvailablePorts();

    @Rule
    public ClearShiroSubject clearShiroSubject = new ClearShiroSubject();

    @ClassRule
    public static ServiceITSetup setup = new ServiceITSetupImpl(cassandraResource);

    @Rule
    public TestName name = new TestName();

    @Test
    public void testCreateOrganizationAndAdminWithConfirmationOnly() throws Exception {
        setup.set(PROPERTIES_SYSADMIN_APPROVES_ADMIN_USERS, "false");
        setup.set(PROPERTIES_SYSADMIN_APPROVES_ORGANIZATIONS, "false");
        setup.set(PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION, "true");
        setup.set(PROPERTIES_SYSADMIN_EMAIL, "sysadmin-1@mockserver.com");
        setup.set(PROPERTIES_NOTIFY_ADMIN_OF_ACTIVATION, "true");

        OrganizationOwnerInfo org_owner = createOwnerAndOrganization(ORGANIZATION_NAME, "test-user-1", "Test User",
                "test-user-1@mockserver.com", "testpassword", false, false);
        assertNotNull(org_owner);

        List<Message> inbox = Mailbox.get("test-user-1@mockserver.com");

        assertFalse(inbox.isEmpty());

        MockImapClient client = new MockImapClient("mockserver.com", "test-user-1", "somepassword");
        client.processMail();

        Message confirmation = inbox.get(0);
        assertEquals("User Account Confirmation: test-user-1@mockserver.com", confirmation.getSubject());

        String token = getTokenFromMessage(confirmation);
        LOG.info(token);

        assertEquals(ActivationState.ACTIVATED,
                setup.getMgmtSvc().handleConfirmationTokenForAdminUser(org_owner.owner.getUuid(), token));

        Message activation = inbox.get(1);
        assertEquals("User Account Activated", activation.getSubject());

        client = new MockImapClient("mockserver.com", "test-user-1", "somepassword");
        client.processMail();
    }

    @Test
    public void testCreateOrganizationAndAdminWithConfirmationAndActivation() throws Exception {
        setup.set(PROPERTIES_SYSADMIN_APPROVES_ADMIN_USERS, "true");
        setup.set(PROPERTIES_NOTIFY_ADMIN_OF_ACTIVATION, "true");
        setup.set(PROPERTIES_SYSADMIN_APPROVES_ORGANIZATIONS, "false");
        setup.set(PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION, "true");
        setup.set(PROPERTIES_SYSADMIN_EMAIL, "sysadmin-2@mockserver.com");

        OrganizationOwnerInfo org_owner = createOwnerAndOrganization(ORGANIZATION_NAME_2, "test-user-2",
                "Test User", "test-user-2@mockserver.com", "testpassword", false, false);
        assertNotNull(org_owner);

        List<Message> user_inbox = Mailbox.get("test-user-2@mockserver.com");

        assertFalse(user_inbox.isEmpty());

        Message confirmation = user_inbox.get(0);
        assertEquals("User Account Confirmation: test-user-2@mockserver.com", confirmation.getSubject());

        String token = getTokenFromMessage(confirmation);
        LOG.info(token);

        ActivationState state = setup.getMgmtSvc().handleConfirmationTokenForAdminUser(org_owner.owner.getUuid(),
                token);
        assertEquals(ActivationState.CONFIRMED_AWAITING_ACTIVATION, state);

        confirmation = user_inbox.get(1);
        assertEquals("User Account Confirmed", confirmation.getSubject());

        List<Message> sysadmin_inbox = Mailbox.get("sysadmin-2@mockserver.com");
        assertFalse(sysadmin_inbox.isEmpty());

        Message activation = sysadmin_inbox.get(0);
        assertEquals("Request For Admin User Account Activation test-user-2@mockserver.com",
                activation.getSubject());

        token = getTokenFromMessage(activation);
        LOG.info(token);

        state = setup.getMgmtSvc().handleActivationTokenForAdminUser(org_owner.owner.getUuid(), token);
        assertEquals(ActivationState.ACTIVATED, state);

        Message activated = user_inbox.get(2);
        assertEquals("User Account Activated", activated.getSubject());

        MockImapClient client = new MockImapClient("mockserver.com", "test-user-2", "somepassword");
        client.processMail();
    }

    @Test
    public void skipAllEmailConfiguration() throws Exception {
        setup.set(PROPERTIES_SYSADMIN_APPROVES_ORGANIZATIONS, "false");
        setup.set(PROPERTIES_ORGANIZATIONS_REQUIRE_CONFIRMATION, "false");
        setup.set(PROPERTIES_SYSADMIN_APPROVES_ADMIN_USERS, "false");
        setup.set(PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION, "false");

        OrganizationOwnerInfo ooi = setup.getMgmtSvc().createOwnerAndOrganization("org-skipallemailtest",
                "user-skipallemailtest", "name-skipallemailtest", "nate+skipallemailtest@apigee.com", "password");

        EntityManager em = setup.getEmf().getEntityManager(CassandraService.MANAGEMENT_APPLICATION_ID);
        User user = em.get(ooi.getOwner().getUuid(), User.class);
        assertTrue(user.activated());
        assertFalse(user.disabled());
        assertTrue(user.confirmed());
    }

    @Test
    public void testEmailStrings() {
        testProperty(PROPERTIES_EMAIL_ADMIN_ACTIVATED, false);
        testProperty(PROPERTIES_EMAIL_ADMIN_CONFIRMATION, true);
        testProperty(PROPERTIES_EMAIL_ADMIN_PASSWORD_RESET, true);
        testProperty(PROPERTIES_EMAIL_ADMIN_USER_ACTIVATION, true);
        testProperty(PROPERTIES_EMAIL_ORGANIZATION_ACTIVATED, true);
        testProperty(PROPERTIES_EMAIL_ORGANIZATION_CONFIRMATION, true);
        testProperty(PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATION, true);
        testProperty(PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATION, true);
        testProperty(PROPERTIES_EMAIL_USER_ACTIVATED, false);
        testProperty(PROPERTIES_EMAIL_USER_CONFIRMATION, true);
        testProperty(PROPERTIES_EMAIL_USER_PASSWORD_RESET, true);
        testProperty(PROPERTIES_EMAIL_USER_PIN_REQUEST, true);
    }

    @Test
    public void testAppUserActivationResetpwdMail() throws Exception {
        String orgName = this.getClass().getName() + "1";
        String appName = name.getMethodName();
        String userName = "Test User";
        String email = "test-user-4@mockserver.com";
        String passwd = "testpassword";
        OrganizationOwnerInfo orgOwner;

        orgOwner = createOwnerAndOrganization(orgName, appName, userName, email, passwd, false, false);
        assertNotNull(orgOwner);

        ApplicationInfo app = setup.getMgmtSvc().createApplication(orgOwner.getOrganization().getUuid(), appName);
        enableAdminApproval(app.getId());
        User user = setupAppUser(app.getId(), "testAppUserMailUrl", "testAppUserMailUrl@test.com", false);

        String subject = "Request For User Account Activation testAppUserMailUrl@test.com";
        String activation_url = String.format(setup.get(PROPERTIES_USER_ACTIVATION_URL), orgName, appName,
                user.getUuid().toString());

        // Activation
        setup.getMgmtSvc().startAppUserActivationFlow(app.getId(), user);

        List<Message> inbox = Mailbox.get(email);
        assertFalse(inbox.isEmpty());
        MockImapClient client = new MockImapClient("usergrid.com", "test", "somepassword");
        client.processMail();

        // subject ok
        Message activation = inbox.get(0);
        assertEquals(subject, activation.getSubject());

        // activation url ok
        String mailContent = (String) ((MimeMultipart) activation.getContent()).getBodyPart(1).getContent();
        LOG.info(mailContent);
        assertTrue(StringUtils.contains(mailContent, activation_url));

        // token ok
        String token = getTokenFromMessage(activation);
        LOG.info(token);
        ActivationState activeState = setup.getMgmtSvc().handleActivationTokenForAppUser(app.getId(),
                user.getUuid(), token);
        assertEquals(ActivationState.ACTIVATED, activeState);

        subject = "Password Reset";
        String reset_url = String.format(setup.get(PROPERTIES_USER_RESETPW_URL), orgName, appName,
                user.getUuid().toString());

        // reset_pwd
        setup.getMgmtSvc().startAppUserPasswordResetFlow(app.getId(), user);

        inbox = Mailbox.get("testAppUserMailUrl@test.com");
        assertFalse(inbox.isEmpty());
        client = new MockImapClient("test.com", "testAppUserMailUrl", "somepassword");
        client.processMail();

        // subject ok
        Message reset = inbox.get(1);
        assertEquals(subject, reset.getSubject());

        // resetpwd url ok
        mailContent = (String) ((MimeMultipart) reset.getContent()).getBodyPart(1).getContent();
        LOG.info(mailContent);
        assertTrue(StringUtils.contains(mailContent, reset_url));

        // token ok
        token = getTokenFromMessage(reset);
        LOG.info(token);
        assertTrue(setup.getMgmtSvc().checkPasswordResetTokenForAppUser(app.getId(), user.getUuid(), token));

        // ensure revoke works
        setup.getMgmtSvc().revokeAccessTokenForAppUser(token);
        assertFalse(setup.getMgmtSvc().checkPasswordResetTokenForAppUser(app.getId(), user.getUuid(), token));
    }

    /** Tests to make sure a normal user must be activated by the admin after confirmation. */
    @Test
    public void testAppUserConfirmationMail() throws Exception {
        String orgName = this.getClass().getName();
        String appName = name.getMethodName();
        String userName = "Test User";
        String email = "test-user-45@mockserver.com";
        String passwd = "testpassword";
        OrganizationOwnerInfo orgOwner;

        orgOwner = createOwnerAndOrganization(orgName, appName, userName, email, passwd, false, false);
        assertNotNull(orgOwner);

        ApplicationInfo app = setup.getMgmtSvc().createApplication(orgOwner.getOrganization().getUuid(), appName);
        assertNotNull(app);
        enableEmailConfirmation(app.getId());
        enableAdminApproval(app.getId());
        User user = setupAppUser(app.getId(), "testAppUserConfMail", "testAppUserConfMail@test.com", true);

        String subject = "User Account Confirmation: testAppUserConfMail@test.com";
        String urlProp = setup.get(PROPERTIES_USER_CONFIRMATION_URL);
        String confirmation_url = String.format(urlProp, orgName, appName, user.getUuid().toString());

        // request confirmation
        setup.getMgmtSvc().startAppUserActivationFlow(app.getId(), user);

        List<Message> inbox = Mailbox.get("testAppUserConfMail@test.com");
        assertFalse(inbox.isEmpty());
        MockImapClient client = new MockImapClient("test.com", "testAppUserConfMail", "somepassword");
        client.processMail();

        // subject ok
        Message confirmation = inbox.get(0);
        assertEquals(subject, confirmation.getSubject());

        // confirmation url ok
        String mailContent = (String) ((MimeMultipart) confirmation.getContent()).getBodyPart(1).getContent();
        LOG.info(mailContent);
        assertTrue(StringUtils.contains(mailContent, confirmation_url));

        // token ok
        String token = getTokenFromMessage(confirmation);
        LOG.info(token);
        ActivationState activeState = setup.getMgmtSvc().handleConfirmationTokenForAppUser(app.getId(),
                user.getUuid(), token);
        assertEquals(ActivationState.CONFIRMED_AWAITING_ACTIVATION, activeState);
    }

    /////////////////////////////
    // Private Utility Methods //
    /////////////////////////////

    private OrganizationOwnerInfo createOwnerAndOrganization(String orgName, String userName, String display,
            String email, String password, boolean disabled, boolean active) throws Exception {
        return setup.getMgmtSvc().createOwnerAndOrganization(orgName, userName, display, email, password, disabled,
                active);
    }

    private String getTokenFromMessage(Message msg) throws IOException, MessagingException {
        String body = ((MimeMultipart) msg.getContent()).getBodyPart(0).getContent().toString();
        // TODO better token extraction
        // this is going to get the wrong string if the first part is not
        // text/plain and the url isn't the last character in the email
        return StringUtils.substringAfterLast(body, "token=");
    }

    private void testProperty(String propertyName, boolean containsSubstitution) {
        String propertyValue = setup.get(propertyName);
        assertTrue(propertyName + " was not found", isNotBlank(propertyValue));
        LOG.info(propertyName + "=" + propertyValue);

        if (containsSubstitution) {
            Map<String, String> valuesMap = new HashMap<String, String>();
            valuesMap.put("reset_url", "test-url");
            valuesMap.put("organization_name", "test-org");
            valuesMap.put("activation_url", "test-url");
            valuesMap.put("confirmation_url", "test-url");
            valuesMap.put("user_email", "test-email");
            valuesMap.put("pin", "test-pin");
            StrSubstitutor sub = new StrSubstitutor(valuesMap);
            String resolvedString = sub.replace(propertyValue);
            assertNotSame(propertyValue, resolvedString);
        }
    }

    private void enableEmailConfirmation(UUID appId) throws Exception {
        EntityManager em = setup.getEmf().getEntityManager(appId);
        SimpleEntityRef ref = new SimpleEntityRef(Application.ENTITY_TYPE, appId);
        em.setProperty(ref, ManagementServiceImpl.REGISTRATION_REQUIRES_EMAIL_CONFIRMATION, true);
    }

    private void enableAdminApproval(UUID appId) throws Exception {
        EntityManager em = setup.getEmf().getEntityManager(appId);
        SimpleEntityRef ref = new SimpleEntityRef(Application.ENTITY_TYPE, appId);
        em.setProperty(ref, ManagementServiceImpl.REGISTRATION_REQUIRES_ADMIN_APPROVAL, true);
    }

    private User setupAppUser(UUID appId, String username, String email, boolean activated) throws Exception {
        Mailbox.clearAll();

        EntityManager em = setup.getEmf().getEntityManager(appId);

        Map<String, Object> userProps = new LinkedHashMap<String, Object>();
        userProps.put("username", username);
        userProps.put("email", email);
        userProps.put("activated", activated);

        return em.create(User.ENTITY_TYPE, User.class, userProps);
    }
}