com.zimbra.qa.unittest.TestCookieReuse.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.qa.unittest.TestCookieReuse.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2014, 2015, 2016 Synacor, Inc.
 *
 * 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, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */
package com.zimbra.qa.unittest;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;

import com.zimbra.client.ZMailbox;
import com.zimbra.common.auth.ZAuthToken;
import com.zimbra.common.httpclient.HttpClientUtil;
import com.zimbra.common.service.ServiceException;
import com.zimbra.common.soap.Element;
import com.zimbra.common.soap.Element.JSONElement;
import com.zimbra.common.soap.Element.XMLElement;
import com.zimbra.common.soap.SoapFaultException;
import com.zimbra.common.soap.SoapHttpTransport;
import com.zimbra.common.soap.SoapProtocol;
import com.zimbra.common.soap.SoapTransport;
import com.zimbra.common.soap.SoapUtil;
import com.zimbra.common.util.ZimbraHttpConnectionManager;
import com.zimbra.common.util.ZimbraLog;
import com.zimbra.cs.account.Account;
import com.zimbra.cs.account.AuthToken;
import com.zimbra.cs.account.Provisioning;
import com.zimbra.cs.account.ZimbraAuthToken;
import com.zimbra.cs.mailbox.MailItem;
import com.zimbra.cs.service.AuthProvider;
import com.zimbra.cs.servlet.util.CsrfUtil;
import com.zimbra.soap.JaxbUtil;
import com.zimbra.soap.account.message.AuthRequest;
import com.zimbra.soap.account.message.AuthResponse;
import com.zimbra.soap.account.message.EndSessionRequest;
import com.zimbra.soap.account.message.GetInfoRequest;
import com.zimbra.soap.admin.message.CreateAccountRequest;
import com.zimbra.soap.mail.message.SearchRequest;
import com.zimbra.soap.mail.message.SearchResponse;
import com.zimbra.soap.type.AccountSelector;
import com.zimbra.soap.type.SearchHit;

public class TestCookieReuse {

    @Rule
    public TestName testInfo = new TestName();

    private static final String NAME_PREFIX = TestUserServlet.class.getSimpleName();
    private static String USER_NAME;
    private static String UNAUTHORIZED_USER;
    private int currentSupportedAuthVersion;

    @Before
    public void setUp() throws Exception {
        currentSupportedAuthVersion = Provisioning.getInstance().getLocalServer().getLowestSupportedAuthVersion();

        String prefix = NAME_PREFIX + "-" + testInfo.getMethodName().toLowerCase() + "-";
        USER_NAME = prefix + "user1";
        UNAUTHORIZED_USER = AccountTestUtil.getAddress(prefix + "unauthorized");
        cleanUp();
        TestUtil.createAccount(USER_NAME);
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        // Add a test message, to make sure the account isn't empty
        TestUtil.addMessage(mbox, NAME_PREFIX);
    }

    @After
    public void tearDown() throws Exception {
        cleanUp();
        Provisioning.getInstance().getLocalServer().setLowestSupportedAuthVersion(currentSupportedAuthVersion);
    }

    private void cleanUp() throws Exception {
        TestUtil.deleteAccountIfExists(USER_NAME);
        TestUtil.deleteAccountIfExists(UNAUTHORIZED_USER);
    }

    public static void main(String[] args) throws Exception {
        TestUtil.cliSetup();
        TestUtil.runTest(TestCookieReuse.class);
    }

    /**
     * Verify that we can use the cookie for REST session if the session is valid
     */
    @Test
    public void testValidCookie() throws ServiceException, IOException {
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss");

        HttpClient client = mbox.getHttpClient(uri);
        GetMethod get = new GetMethod(uri.toString());
        int statusCode = HttpClientUtil.executeMethod(client, get);
        Assert.assertEquals("This request should succeed. Getting status code " + statusCode, HttpStatus.SC_OK,
                statusCode);
    }

    /**
     * Verify that we can RE-use the cookie for REST session if the session is valid
     */
    @Test
    public void testValidSessionCookieReuse() throws ServiceException, IOException {
        //establish legitimate connection
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss");
        HttpClient alice = mbox.getHttpClient(uri);
        //create evesdropper's connection
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        Cookie[] cookies = alice.getState().getCookies();
        HttpState state = new HttpState();
        for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];
            state.addCookie(new Cookie(uri.getHost(), cookie.getName(), cookie.getValue(), "/", null, false));
        }
        eve.setState(state);
        GetMethod get = new GetMethod(uri.toString());
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should succeed. Getting status code " + statusCode, HttpStatus.SC_OK,
                statusCode);
    }

    /**
     * Verify that we canNOT RE-use the cookie for REST session if the session is valid
     */
    @Test
    public void testAutoEndSession() throws ServiceException, IOException {
        //establish legitimate connection
        TestUtil.setAccountAttr(USER_NAME, Provisioning.A_zimbraForceClearCookies, "TRUE");
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss");
        HttpClient alice = mbox.getHttpClient(uri);

        //create evesdropper's connection
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        Cookie[] cookies = alice.getState().getCookies();
        HttpState state = new HttpState();
        for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];
            state.addCookie(new Cookie(uri.getHost(), cookie.getName(), cookie.getValue(), "/", null, false));
        }
        eve.setState(state);
        Account a = TestUtil.getAccount(USER_NAME);
        a.setForceClearCookies(true);

        EndSessionRequest esr = new EndSessionRequest();
        mbox.invokeJaxb(esr);
        GetMethod get = new GetMethod(uri.toString());
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should not succeed. Getting status code " + statusCode,
                HttpStatus.SC_UNAUTHORIZED, statusCode);
    }

    /**
     * Verify that we canNOT RE-use the cookie taken from a legitimate HTTP session for a REST request
     * after ending the original session
     */
    @Test
    public void testForceEndSession() throws ServiceException, IOException {
        //establish legitimate connection
        TestUtil.setAccountAttr(USER_NAME, Provisioning.A_zimbraForceClearCookies, "FALSE");
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss");
        HttpClient alice = mbox.getHttpClient(uri);

        //create evesdropper's connection
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        Cookie[] cookies = alice.getState().getCookies();
        HttpState state = new HttpState();
        for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];
            state.addCookie(new Cookie(uri.getHost(), cookie.getName(), cookie.getValue(), "/", null, false));
        }
        eve.setState(state);
        Account a = TestUtil.getAccount(USER_NAME);
        a.setForceClearCookies(false);

        EndSessionRequest esr = new EndSessionRequest();
        esr.setLogOff(true);
        mbox.invokeJaxb(esr);
        GetMethod get = new GetMethod(uri.toString());
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should not succeed. Getting status code " + statusCode,
                HttpStatus.SC_UNAUTHORIZED, statusCode);
    }

    /**
     * Verify that we canNOT RE-use the cookie taken from a legitimate HTTP session for a SOAP request after
     * ending the original session
     */
    @Test
    public void testInvalidSearchRequest() throws ServiceException, IOException {
        //establish legitimate connection
        TestUtil.setAccountAttr(USER_NAME, Provisioning.A_zimbraForceClearCookies, "FALSE");
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss");
        mbox.getHttpClient(uri);
        ZAuthToken authT = mbox.getAuthToken();

        //create evesdropper's SOAP client
        SoapHttpTransport transport = new HttpCookieSoapTransport(TestUtil.getSoapUrl());
        transport.setAuthToken(authT);

        //check that search returns something
        SearchRequest searchReq = new SearchRequest();
        searchReq.setSearchTypes(MailItem.Type.MESSAGE.toString());
        searchReq.setQuery("in:inbox");

        Element req = JaxbUtil.jaxbToElement(searchReq, SoapProtocol.SoapJS.getFactory());
        Element res = transport.invoke(req);
        SearchResponse searchResp = JaxbUtil.elementToJaxb(res);
        List<SearchHit> searchHits = searchResp.getSearchHits();
        Assert.assertFalse("this search request should return some conversations", searchHits.isEmpty());

        //explicitely end cookie session
        Account a = TestUtil.getAccount(USER_NAME);
        a.setForceClearCookies(false);
        EndSessionRequest esr = new EndSessionRequest();
        esr.setLogOff(true);
        mbox.invokeJaxb(esr);

        //check that search returns nothing
        transport = new HttpCookieSoapTransport(TestUtil.getSoapUrl());
        transport.setAuthToken(authT);
        searchReq = new SearchRequest();
        searchReq.setSearchTypes(MailItem.Type.MESSAGE.toString());
        searchReq.setQuery("in:inbox");
        try {
            req = JaxbUtil.jaxbToElement(searchReq, SoapProtocol.SoapJS.getFactory());
            res = transport.invoke(req);
            searchResp = JaxbUtil.elementToJaxb(res);
            searchHits = searchResp.getSearchHits();
            Assert.assertTrue("this search request should fail", searchHits.isEmpty());
        } catch (SoapFaultException ex) {
            Assert.assertEquals("Should be getting 'auth required' exception", ServiceException.AUTH_EXPIRED,
                    ex.getCode());
        }
    }

    /**
     * Verify that we canNOT RE-use the cookie for REST session after logging out of plain HTML client
     * @throws URISyntaxException
     * @throws InterruptedException
     */
    @Test
    public void testWebLogOut() throws ServiceException, IOException, URISyntaxException, InterruptedException {
        //establish legitimate connection
        TestUtil.setAccountAttr(USER_NAME, Provisioning.A_zimbraForceClearCookies, "FALSE");
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss");
        HttpClient alice = mbox.getHttpClient(uri);

        //create evesdropper's connection
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        Cookie[] cookies = alice.getState().getCookies();
        HttpState state = new HttpState();
        for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];
            state.addCookie(new Cookie(uri.getHost(), cookie.getName(), cookie.getValue(), "/", null, false));
        }
        eve.setState(state);
        Account a = TestUtil.getAccount(USER_NAME);
        a.setForceClearCookies(false);
        URI logoutUri = new URI(String.format("%s://%s%s/?loginOp=logout", uri.getScheme(), uri.getHost(),
                (uri.getPort() > 80 ? (":" + uri.getPort()) : "")));
        GetMethod logoutMethod = new GetMethod(logoutUri.toString());
        int statusCode = alice.executeMethod(logoutMethod);
        Assert.assertEquals("Log out request should succeed. Getting status code " + statusCode, HttpStatus.SC_OK,
                statusCode);
        GetMethod get = new GetMethod(uri.toString());
        statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should not succeed. Getting status code " + statusCode,
                HttpStatus.SC_UNAUTHORIZED, statusCode);
    }

    /**
     * test registering an authtoken
     * @throws Exception
     */
    @Test
    public void testTokenRegistration() throws Exception {
        Account a = TestUtil.getAccount(USER_NAME);
        ZimbraAuthToken at = new ZimbraAuthToken(a);
        Assert.assertTrue("token should be registered", at.isRegistered());
    }

    /**
     * test de-registering an authtoken
     * @throws Exception
     */
    @Test
    public void testTokenDeregistration() throws Exception {
        Account a = TestUtil.getAccount(USER_NAME);
        ZimbraAuthToken at = new ZimbraAuthToken(a);
        Assert.assertTrue("token should be registered", at.isRegistered());
        at.deRegister();
        Assert.assertFalse("token should not be registered", at.isRegistered());
    }

    /**
     * test de-registering an admin authtoken
     * @throws Exception
     */
    @Test
    public void testAdminTokenDeregistration() throws Exception {
        AuthToken at = AuthProvider.getAdminAuthToken();
        Assert.assertTrue("token should be registered", at.isRegistered());
        at.deRegister();
        Assert.assertFalse("token should not be registered", at.isRegistered());
    }

    /**
     * test token being deregistered after it is expired
     * @throws Exception
     */
    @Test
    public void testTokenExpiredTokenDeregistration() throws Exception {
        Account a = TestUtil.getAccount(USER_NAME);
        ZimbraAuthToken at = new ZimbraAuthToken(a, System.currentTimeMillis() - 1000);

        ZimbraAuthToken at2 = new ZimbraAuthToken(a, System.currentTimeMillis() + 10000);

        Assert.assertFalse("First token should not be registered", at.isRegistered());
        Assert.assertTrue("Second token should be registered", at2.isRegistered());
    }

    /**
     * Test old behavior: tokens appear to be registered even when they are not registered when lowest
     * supported auth version is set to 1
     * @throws Exception
     */
    @Test
    public void testOldClientSupport() throws Exception {
        Account a = TestUtil.getAccount(USER_NAME);
        ZimbraAuthToken at = new ZimbraAuthToken(a, System.currentTimeMillis() - 1000);
        Assert.assertTrue("token should be registered", at.isRegistered());
        at.deRegister();
        Assert.assertFalse("token should not be registered", at.isRegistered());

        //lowering supported auth version should allow unregistered cookies
        Provisioning.getInstance().getLocalServer().setLowestSupportedAuthVersion(1);
        Assert.assertTrue("token should appear to be registered", at.isRegistered());

        //raising supported auth version should not allow unregistered cookies
        Provisioning.getInstance().getLocalServer().setLowestSupportedAuthVersion(2);
        Assert.assertFalse("token should not be registered", at.isRegistered());
    }

    /**
     * Verify that when zimbraForceClearCookies is set to TRUE authtokens get deregistered
     * @throws Exception
     */
    @Test
    public void testClearCookies() throws Exception {
        Account a = TestUtil.getAccount(USER_NAME);
        a.setForceClearCookies(true);
        ZimbraAuthToken at = new ZimbraAuthToken(a);
        Assert.assertTrue("token should be registered", at.isRegistered());
        at.deRegister();
        Assert.assertFalse("token should not be registered", at.isRegistered());
    }

    /**
     * Verify that when an expired authtoken has been removed from LDAP, login still succeeds
     * @throws Exception
     */
    @Test
    public void testLoginClearAuthTokensException() throws Exception {
        Account a = TestUtil.getAccount(USER_NAME);
        ZimbraAuthToken at1 = new ZimbraAuthToken(a, System.currentTimeMillis() + 1000);
        Assert.assertFalse("token should not be expired yet", at1.isExpired());
        Thread.sleep(2000);
        Assert.assertTrue("token should have expired by now", at1.isExpired());

        //explicitely clean up expired auth tokens
        a.purgeAuthTokens();

        //verify that AuthRequest still works
        SoapHttpTransport transport = new SoapHttpTransport(TestUtil.getSoapUrl());
        AccountSelector acctSel = new AccountSelector(com.zimbra.soap.type.AccountBy.name, a.getName());
        AuthRequest req = new AuthRequest(acctSel, "test123");
        Element resp = transport.invoke(JaxbUtil.jaxbToElement(req, SoapProtocol.SoapJS.getFactory()));
        AuthResponse authResp = JaxbUtil.elementToJaxb(resp);
        String newAuthToken = authResp.getAuthToken();
        Assert.assertNotNull("should have received a new authtoken", newAuthToken);
        AuthToken at = ZimbraAuthToken.getAuthToken(newAuthToken);
        Assert.assertTrue("new auth token should be registered", at.isRegistered());
        Assert.assertFalse("new auth token should not be expired yet", at.isExpired());
    }

    /**
     * Verify that we CANNOT make an unauthorized admin GET request without an admin cookie
     */
    @Test
    public void testGetWithoutAdminCookie() throws Exception {
        int port = 7071;
        try {
            port = Provisioning.getInstance().getLocalServer().getIntAttr(Provisioning.A_zimbraAdminPort, 0);
        } catch (ServiceException e) {
            ZimbraLog.test.error("Unable to get admin SOAP port", e);
        }
        String getServerConfigURL = "https://localhost:" + port + "/service/collectconfig/?host="
                + Provisioning.getInstance().getLocalServer().getName();
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        GetMethod get = new GetMethod(getServerConfigURL);
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should NOT succeed. Getting status code " + statusCode,
                HttpStatus.SC_UNAUTHORIZED, statusCode);
    }

    /**
     * Verify that we CAN make an admin GET request by re-using a valid non-csrf-enabled cookie
     */
    @Test
    public void testReuseAdminCookieWithoutCsrf() throws Exception {
        AuthToken at = AuthProvider.getAdminAuthToken();
        at.setCsrfTokenEnabled(false);
        int port = 7071;
        try {
            port = Provisioning.getInstance().getLocalServer().getIntAttr(Provisioning.A_zimbraAdminPort, 0);
        } catch (ServiceException e) {
            ZimbraLog.test.error("Unable to get admin SOAP port", e);
        }
        String host = Provisioning.getInstance().getLocalServer().getName();
        String getServerConfigURL = "https://localhost:" + port + "/service/collectconfig/?host=" + host;
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        HttpState state = new HttpState();
        at.encode(state, true, "localhost");
        eve.setState(state);
        GetMethod get = new GetMethod(getServerConfigURL);
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should succeed. Getting status code " + statusCode, HttpStatus.SC_OK,
                statusCode);
    }

    /**
     * Verify that we CAN make a GET request by reusing a valid non-csrf-enabled cookie
     */
    @Test
    public void testReuseUserCookieWithoutCsrf() throws Exception {
        AuthToken at = AuthProvider.getAuthToken(TestUtil.getAccount(USER_NAME));
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss&thief=false");
        at.setCsrfTokenEnabled(false);
        GetMethod get = new GetMethod(uri.toString());
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        HttpState state = HttpClientUtil.newHttpState(new ZAuthToken(at.getEncoded()), uri.getHost(), false);
        eve.setState(state);
        eve.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should succeed. Getting status code " + statusCode + " Response: "
                + get.getResponseBodyAsString(), HttpStatus.SC_OK, statusCode);
    }

    /**
     * Verify that we CAN make a GET request by reusing a valid CSRF-enabled cookie
     */
    @Test
    public void testReuseUserCookieWithCsrf() throws Exception {
        AuthToken at = AuthProvider.getAuthToken(TestUtil.getAccount(USER_NAME));
        ZMailbox mbox = TestUtil.getZMailbox(USER_NAME);
        URI uri = mbox.getRestURI("Inbox?fmt=rss&thief=true");
        at.setCsrfTokenEnabled(true);
        GetMethod get = new GetMethod(uri.toString());
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        HttpState state = HttpClientUtil.newHttpState(new ZAuthToken(at.getEncoded()), uri.getHost(), false);
        eve.setState(state);
        eve.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should succeed. Getting status code " + statusCode + " Response: "
                + get.getResponseBodyAsString(), HttpStatus.SC_OK, statusCode);
    }

    /**
     * Verify that we CAN make an admin GET request by reusing a valid csrf-enabled cookie
     */
    @Test
    public void testReuseAdminCookieWithCsrf() throws Exception {
        AuthToken at = AuthProvider.getAdminAuthToken();
        at.setCsrfTokenEnabled(true);
        int port = 7071;
        try {
            port = Provisioning.getInstance().getLocalServer().getIntAttr(Provisioning.A_zimbraAdminPort, 0);
        } catch (ServiceException e) {
            ZimbraLog.test.error("Unable to get admin SOAP port", e);
        }
        String host = Provisioning.getInstance().getLocalServer().getName();
        String getServerConfigURL = "https://localhost:" + port + "/service/collectconfig/?host=" + host;
        HttpClient eve = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        HttpState state = new HttpState();
        at.encode(state, true, "localhost");
        eve.setState(state);
        GetMethod get = new GetMethod(getServerConfigURL);
        int statusCode = HttpClientUtil.executeMethod(eve, get);
        Assert.assertEquals("This request should succeed. Getting status code " + statusCode, HttpStatus.SC_OK,
                statusCode);
    }

    /**
     * Verify that we CANNOT make an admin POST request by reusing a valid csrf-enabled cookie without a csrf token
     */
    @Test
    public void testUnauthorizedAdminPostWithCsrf() throws Exception {
        AuthToken at = AuthProvider.getAdminAuthToken();
        at.setCsrfTokenEnabled(true);
        SoapTransport transport = TestUtil.getAdminSoapTransport();
        transport.setAuthToken(at.getEncoded());
        Map<String, Object> attrs = null;
        CreateAccountRequest request = new CreateAccountRequest(UNAUTHORIZED_USER, "test123", attrs);
        try {
            transport.invoke(JaxbUtil.jaxbToElement(request));
        } catch (ServiceException e) {
            Assert.assertEquals("should be catching AUTH EXPIRED here", ServiceException.AUTH_REQUIRED,
                    e.getCode());
            return;
        }
        Assert.fail("should have caught an exception");
    }

    /**
     * Verify that we CANNOT make an POST request with a non-CSRF-enabled auth token if the auth token
     * has an associated CSRF token
     */
    @Test
    public void testForgedNonCSRFPost() throws Exception {
        AuthToken at = AuthProvider.getAuthToken(TestUtil.getAccount(USER_NAME));
        at.setCsrfTokenEnabled(false);
        CsrfUtil.generateCsrfToken(at.getAccountId(), at.getExpires(), new Random().nextInt() + 1, at);
        SoapHttpTransport transport = new SoapHttpTransport(TestUtil.getSoapUrl());
        transport.setAuthToken(at.getEncoded());
        GetInfoRequest request = new GetInfoRequest();
        try {
            transport.invoke(JaxbUtil.jaxbToElement(request));
        } catch (ServiceException e) {
            Assert.assertEquals("should be catching AUTH EXPIRED here", ServiceException.AUTH_REQUIRED,
                    e.getCode());
            return;
        }
        Assert.fail("should have caught an exception");
    }

    /**
     * Verify that we CANNOT make an admin POST request with a non-CSRF-enabled auth token if
     * the auth token has an associated CSRF token
     */
    @Test
    public void testForgedNonCSRFAdminPost() throws Exception {
        AuthToken at = AuthProvider.getAdminAuthToken();
        at.setCsrfTokenEnabled(false);
        CsrfUtil.generateCsrfToken(at.getAccountId(), at.getExpires(), new Random().nextInt() + 1, at);
        SoapTransport transport = TestUtil.getAdminSoapTransport();
        transport.setAuthToken(at.getEncoded());
        Map<String, Object> attrs = null;
        CreateAccountRequest request = new CreateAccountRequest(UNAUTHORIZED_USER, "test123", attrs);
        try {
            transport.invoke(JaxbUtil.jaxbToElement(request));
        } catch (ServiceException e) {
            Assert.assertEquals("should be catching AUTH EXPIRED here", ServiceException.AUTH_REQUIRED,
                    e.getCode());
            return;
        }
        Assert.fail("should have caught an exception");
    }

    /**
     * version of SOAP transport that uses HTTP cookies instead of SOAP message header for transporting Auth Token
     * @author gsolovyev
     */
    private class HttpCookieSoapTransport extends SoapHttpTransport {

        public HttpCookieSoapTransport(String uri) {
            super(uri);
        }

        @Override
        protected final Element generateSoapMessage(Element document, boolean raw, boolean noSession,
                String requestedAccountId, String changeToken, String tokenType) {

            // don't use the default protocol version if it's incompatible with the passed-in request
            SoapProtocol proto = getRequestProtocol();
            if (proto == SoapProtocol.SoapJS) {
                if (document instanceof XMLElement)
                    proto = SoapProtocol.Soap12;
            } else {
                if (document instanceof JSONElement)
                    proto = SoapProtocol.SoapJS;
            }
            SoapProtocol responseProto = getResponseProtocol() == null ? proto : getResponseProtocol();

            String targetId = requestedAccountId != null ? requestedAccountId : getTargetAcctId();
            String targetName = targetId == null ? getTargetAcctName() : null;

            Element context = null;
            if (generateContextHeader()) {
                context = SoapUtil.toCtxt(proto, null, null);
                if (noSession) {
                    SoapUtil.disableNotificationOnCtxt(context);
                } else {
                    SoapUtil.addSessionToCtxt(context, getAuthToken() == null ? null : getSessionId(),
                            getMaxNotifySeq());
                }
                SoapUtil.addTargetAccountToCtxt(context, targetId, targetName);
                SoapUtil.addChangeTokenToCtxt(context, changeToken, tokenType);
                SoapUtil.addUserAgentToCtxt(context, getUserAgentName(), getUserAgentVersion());
                if (responseProto != proto) {
                    SoapUtil.addResponseProtocolToCtxt(context, responseProto);
                }

            }
            Element envelope = proto.soapEnvelope(document, context);
            return envelope;
        }
    }
}