Search in sources :

Example 11 with SoapProtocol

use of com.zimbra.common.soap.SoapProtocol in project zm-mailbox by Zimbra.

the class SoapEngine method dispatch.

/**
     * dispatch to the given serviceName the specified document,
     * which should be a soap envelope containing a document to
     * execute.
     *
     * @param path  the path (i.e., /service/foo) of the service to dispatch to
     * @param envelope the top-level element of the message
     * @param context user context parameters
     * @return an XmlObject which is a SoapEnvelope containing the response
     * @throws CsrfTokenException if CSRF token validation fails
     */
private Element dispatch(String path, Element envelope, Map<String, Object> context) {
    SoapProtocol soapProto = SoapProtocol.determineProtocol(envelope);
    if (soapProto == null) {
        // FIXME: have to pick 1.1 or 1.2 since we can't parse any
        soapProto = SoapProtocol.Soap12;
        return soapFaultEnv(soapProto, "SOAP exception", ServiceException.INVALID_REQUEST("unable to determine SOAP version", null));
    }
    Element doc = soapProto.getBodyElement(envelope);
    if (doc == null) {
        return soapFaultEnv(soapProto, "SOAP exception", ServiceException.INVALID_REQUEST("No SOAP body", null));
    }
    ServletRequest servReq = (ServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
    //Check if this handler requires authentication.
    //Do not perform CSRF checks for handlers that do not require authentication
    DocumentHandler handler = dispatcher.getHandler(doc);
    ZimbraSoapContext zsc = null;
    Element ectxt = soapProto.getHeader(envelope, HeaderConstants.CONTEXT);
    try {
        zsc = new ZimbraSoapContext(ectxt, doc.getQName(), handler, context, soapProto);
    } catch (ServiceException e) {
        return soapFaultEnv(soapProto, "unable to construct SOAP context", e);
    }
    boolean doCsrfCheck = false;
    if (servReq.getAttribute(CsrfFilter.CSRF_TOKEN_CHECK) != null) {
        doCsrfCheck = (Boolean) servReq.getAttribute(CsrfFilter.CSRF_TOKEN_CHECK);
    } else if (zsc.getAuthToken() != null && zsc.getAuthToken().isCsrfTokenEnabled()) {
        doCsrfCheck = true;
    }
    if (handler == null) {
        // handler, all other request should be mapped to a Handler
        if (!doc.getName().equals("BatchRequest")) {
            doCsrfCheck = false;
        } else {
            LOG.info("Only BatchRequest does not have a handler mapped to it. Request: %s, does not have a " + "handler, log for future handling.", path);
        }
    } else {
        if (doc.getName().equals("AuthRequest")) {
            // this is a Auth request, no CSRF validation happens
            doCsrfCheck = false;
        } else {
            doCsrfCheck = doCsrfCheck && handler.needsAuth(context);
        }
    }
    if (doCsrfCheck) {
        try {
            HttpServletRequest httpReq = (HttpServletRequest) servReq;
            // Bug: 96167 SoapEngine should be able to read CSRF token from HTTP headers
            String csrfToken = httpReq.getHeader(Constants.CSRF_TOKEN);
            if (StringUtil.isNullOrEmpty(csrfToken)) {
                Element contextElmt = soapProto.getHeader(envelope).getElement(HeaderConstants.E_CONTEXT);
                csrfToken = contextElmt.getAttribute(HeaderConstants.E_CSRFTOKEN);
            }
            AuthToken authToken = zsc.getAuthToken();
            if (!CsrfUtil.isValidCsrfToken(csrfToken, authToken)) {
                LOG.info("CSRF token validation failed for account");
                return soapFaultEnv(soapProto, "cannot dispatch request", ServiceException.AUTH_REQUIRED());
            }
        } catch (ServiceException e) {
            // we came here which implies clients supports CSRF authorization
            // and CSRF token is generated
            LOG.info("Error during CSRF validation.", e);
            return soapFaultEnv(soapProto, "cannot dispatch request", ServiceException.AUTH_REQUIRED());
        }
    }
    SoapProtocol responseProto = zsc.getResponseProtocol();
    String rid = zsc.getRequestedAccountId();
    String proxyAuthToken = null;
    if (rid != null) {
        Provisioning prov = Provisioning.getInstance();
        AccountUtil.addAccountToLogContext(prov, rid, ZimbraLog.C_NAME, ZimbraLog.C_ID, zsc.getAuthToken());
        String aid = zsc.getAuthtokenAccountId();
        if (aid != null && !rid.equals(aid)) {
            AccountUtil.addAccountToLogContext(prov, aid, ZimbraLog.C_ANAME, ZimbraLog.C_AID, zsc.getAuthToken());
        } else if (zsc.getAuthToken() != null && zsc.getAuthToken().getAdminAccountId() != null) {
            AccountUtil.addAccountToLogContext(prov, zsc.getAuthToken().getAdminAccountId(), ZimbraLog.C_ANAME, ZimbraLog.C_AID, zsc.getAuthToken());
        }
        try {
            Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(rid, false);
            if (mbox != null) {
                ZimbraLog.addMboxToContext(mbox.getId());
            }
        } catch (ServiceException ignore) {
        }
        try {
            AuthToken at = zsc.getAuthToken();
            if (at != null) {
                proxyAuthToken = prov.getProxyAuthToken(rid, context);
                at.setProxyAuthToken(proxyAuthToken);
            }
        } catch (ServiceException e) {
            LOG.warn("failed to set proxy auth token: %s", e.getMessage());
        }
    }
    if (zsc.getUserAgent() != null) {
        ZimbraLog.addUserAgentToContext(zsc.getUserAgent());
    }
    if (zsc.getVia() != null) {
        ZimbraLog.addViaToContext(zsc.getVia());
    }
    if (zsc.getSoapRequestId() != null) {
        ZimbraLog.addSoapIdToContext(zsc.getSoapRequestId());
    }
    logRequest(context, envelope);
    context.put(ZIMBRA_CONTEXT, zsc);
    context.put(ZIMBRA_ENGINE, this);
    HttpServletRequest servletRequest = (HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
    boolean isResumed = !ContinuationSupport.getContinuation(servletRequest).isInitial();
    Element responseBody = null;
    if (!zsc.isProxyRequest()) {
        // if the client's told us that they've seen through notification block 50, we can drop old notifications up to that point
        acknowledgeNotifications(zsc);
        if (doc.getQName().equals(ZimbraNamespace.E_BATCH_REQUEST)) {
            boolean contOnError = doc.getAttribute(ZimbraNamespace.A_ONERROR, ZimbraNamespace.DEF_ONERROR).equals("continue");
            responseBody = zsc.createElement(ZimbraNamespace.E_BATCH_RESPONSE);
            if (!isResumed) {
                ZimbraLog.soap.info(doc.getName());
            }
            for (Element req : doc.listElements()) {
                String id = req.getAttribute(A_REQUEST_CORRELATOR, null);
                long start = System.currentTimeMillis();
                Element br = dispatchRequest(dispatcher.getHandler(req), req, context, zsc);
                if (!isResumed) {
                    ZimbraLog.soap.info("(batch) %s elapsed=%d", req.getName(), System.currentTimeMillis() - start);
                }
                if (id != null) {
                    br.addAttribute(A_REQUEST_CORRELATOR, id);
                }
                responseBody.addNonUniqueElement(br);
                if (!contOnError && responseProto.isFault(br)) {
                    break;
                }
                if (proxyAuthToken != null) {
                    // requests will invalidate it when proxying locally;
                    // make sure it's set for each sub-request in batch
                    zsc.getAuthToken().setProxyAuthToken(proxyAuthToken);
                }
            }
        } else {
            String id = doc.getAttribute(A_REQUEST_CORRELATOR, null);
            long start = System.currentTimeMillis();
            responseBody = dispatchRequest(handler, doc, context, zsc);
            if (!isResumed) {
                ZimbraLog.soap.info("%s elapsed=%d", doc.getName(), System.currentTimeMillis() - start);
            }
            if (id != null) {
                responseBody.addAttribute(A_REQUEST_CORRELATOR, id);
            }
        }
    } else {
        // We stick to local server's session when talking to the client.
        try {
            // Detach doc from its current parent, because it will be added as a child element of a new SOAP
            // envelope in the proxy dispatcher.  IllegalAddException will be thrown if we don't detach it first.
            doc.detach();
            ZimbraSoapContext zscTarget = new ZimbraSoapContext(zsc, zsc.getRequestedAccountId()).disableNotifications();
            long start = System.currentTimeMillis();
            responseBody = zsc.getProxyTarget().dispatch(doc, zscTarget);
            ZimbraLog.soap.info("%s proxy=%s,elapsed=%d", doc.getName(), zsc.getProxyTarget(), System.currentTimeMillis() - start);
            responseBody.detach();
        } catch (SoapFaultException e) {
            responseBody = e.getFault() != null ? e.getFault().detach() : responseProto.soapFault(e);
            LOG.debug("proxy handler exception", e);
        } catch (ServiceException e) {
            responseBody = responseProto.soapFault(e);
            LOG.info("proxy handler exception", e);
        } catch (Throwable e) {
            responseBody = responseProto.soapFault(ServiceException.FAILURE(e.toString(), e));
            if (e instanceof OutOfMemoryError) {
                Zimbra.halt("proxy handler exception", e);
            }
            LOG.warn("proxy handler exception", e);
        }
    }
    // put notifications (new sessions and incremental change notifications) to header...
    Element responseHeader = generateResponseHeader(zsc);
    // ... and return the composed response
    return responseProto.soapEnvelope(responseBody, responseHeader);
}
Also used : HttpServletRequest(javax.servlet.http.HttpServletRequest) ServletRequest(javax.servlet.ServletRequest) Element(com.zimbra.common.soap.Element) SoapProtocol(com.zimbra.common.soap.SoapProtocol) Provisioning(com.zimbra.cs.account.Provisioning) SoapFaultException(com.zimbra.common.soap.SoapFaultException) HttpServletRequest(javax.servlet.http.HttpServletRequest) AdminDocumentHandler(com.zimbra.cs.service.admin.AdminDocumentHandler) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) Mailbox(com.zimbra.cs.mailbox.Mailbox) AuthToken(com.zimbra.cs.account.AuthToken)

Example 12 with SoapProtocol

use of com.zimbra.common.soap.SoapProtocol in project zm-mailbox by Zimbra.

the class SoapEngine method dispatch.

public Element dispatch(String path, byte[] soapMessage, Map<String, Object> context) throws CsrfTokenException {
    if (soapMessage == null || soapMessage.length == 0) {
        SoapProtocol soapProto = SoapProtocol.Soap12;
        return soapFaultEnv(soapProto, "SOAP exception", ServiceException.PARSE_ERROR("empty request payload", null));
    }
    InputStream in = new ByteArrayInputStream(soapMessage);
    Element document = null;
    try {
        if (soapMessage[0] == '<') {
            document = Element.parseXML(in);
        } else {
            document = Element.parseJSON(in);
        }
    } catch (SoapParseException e) {
        SoapProtocol soapProto = SoapProtocol.SoapJS;
        logUnparsableRequest(context, soapMessage, e.getMessage());
        return soapFaultEnv(soapProto, "SOAP exception", ServiceException.PARSE_ERROR(e.getMessage(), e));
    } catch (XmlParseException e) {
        logUnparsableRequest(context, soapMessage, e.getMessage());
        SoapProtocol soapProto = chooseFaultProtocolFromBadXml(new ByteArrayInputStream(soapMessage));
        return soapFaultEnv(soapProto, "SOAP exception", e);
    }
    Element resp = dispatch(path, document, context);
    /*
         * For requests(e.g. AuthRequest) that don't have account info in time when they
         * are normally added to the logging context in dispatch after zsc is established
         * from the SOAP request header.  Thus account logging for zimbra.soap won't be
         * effective when the SOAP request is logged in TRACE level normally.
         *
         * For AuthRequest, we call Account.addAccountToLogContext from the handler as
         * soon as the account, which is only available in the SOAP body, is discovered.
         * Account info should be available after dispatch() so account logger can be
         * triggered.
         */
    logRequest(context, document);
    return resp;
}
Also used : ByteArrayInputStream(java.io.ByteArrayInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) InputStream(java.io.InputStream) Element(com.zimbra.common.soap.Element) SoapProtocol(com.zimbra.common.soap.SoapProtocol) SoapParseException(com.zimbra.common.soap.SoapParseException) XmlParseException(com.zimbra.common.soap.XmlParseException)

Example 13 with SoapProtocol

use of com.zimbra.common.soap.SoapProtocol in project zm-mailbox by Zimbra.

the class SoapServlet method sendResponse.

private void sendResponse(HttpServletRequest req, HttpServletResponse resp, Element envelope) throws IOException {
    SoapProtocol soapProto = SoapProtocol.determineProtocol(envelope);
    int statusCode = soapProto.hasFault(envelope) ? HttpServletResponse.SC_INTERNAL_SERVER_ERROR : HttpServletResponse.SC_OK;
    boolean chunkingEnabled = LC.soap_response_chunked_transfer_encoding_enabled.booleanValue();
    if (chunkingEnabled) {
        // disable chunking if proto < HTTP 1.1
        String proto = req.getProtocol();
        try {
            HttpVersion httpVer = HttpVersion.parse(proto);
            chunkingEnabled = !httpVer.lessEquals(HttpVersion.HTTP_1_0);
        } catch (ProtocolException e) {
            ZimbraLog.soap.warn("cannot parse http version in request: %s, http chunked transfer encoding disabled", proto, e);
            chunkingEnabled = false;
        }
    }
    // use jetty default if the LC key is not set
    int responseBufferSize = soapResponseBufferSize();
    if (responseBufferSize != -1)
        resp.setBufferSize(responseBufferSize);
    resp.setContentType(soapProto.getContentType());
    resp.setStatus(statusCode);
    resp.setHeader("Cache-Control", "no-store, no-cache");
    if (chunkingEnabled) {
        // Let jetty chunk the response if applicable.
        ZimbraServletOutputStream out = new ZimbraServletOutputStream(resp.getOutputStream());
        envelope.output(out);
        out.flush();
    } else {
        // serialize the envelope to a byte array and send the response with Content-Length header.
        byte[] soapBytes = envelope.toUTF8();
        resp.setContentLength(soapBytes.length);
        resp.getOutputStream().write(soapBytes);
        resp.getOutputStream().flush();
    }
    envelope.destroy();
}
Also used : ProtocolException(org.apache.commons.httpclient.ProtocolException) ZimbraServletOutputStream(com.zimbra.common.util.ZimbraServletOutputStream) SoapProtocol(com.zimbra.common.soap.SoapProtocol) HttpVersion(org.apache.commons.httpclient.HttpVersion)

Example 14 with SoapProtocol

use of com.zimbra.common.soap.SoapProtocol in project zm-mailbox by Zimbra.

the class ProxyTarget method execute.

public Pair<Element, Element> execute(Element request, ZimbraSoapContext zsc) throws ServiceException {
    if (zsc == null)
        return new Pair<Element, Element>(null, dispatch(request));
    SoapProtocol proto = request instanceof Element.JSONElement ? SoapProtocol.SoapJS : SoapProtocol.Soap12;
    if (proto == SoapProtocol.Soap12 && zsc.getRequestProtocol() == SoapProtocol.Soap11) {
        proto = SoapProtocol.Soap11;
    }
    /* Bug 77604 When a user has been configured to change their password on next login, the resulting proxied
         * ChangePasswordRequest was failing because account was specified in context but no authentication token
         * was supplied.  The server handler rejects a context which has account information but no authentication
         * info - see ZimbraSoapContext constructor - solution is to exclude the account info from the context.
         */
    boolean excludeAccountDetails = AccountConstants.CHANGE_PASSWORD_REQUEST.equals(request.getQName());
    Element envelope = proto.soapEnvelope(request, zsc.toProxyContext(proto, excludeAccountDetails));
    SoapHttpTransport transport = null;
    try {
        transport = new SoapHttpTransport(mURL);
        transport.setTargetAcctId(zsc.getRequestedAccountId());
        if (mMaxAttempts > 0)
            transport.setRetryCount(mMaxAttempts);
        if (mTimeout >= 0)
            transport.setTimeout((int) Math.min(mTimeout, Integer.MAX_VALUE));
        transport.setResponseProtocol(zsc.getResponseProtocol());
        AuthToken authToken = AuthToken.getCsrfUnsecuredAuthToken(zsc.getAuthToken());
        if (authToken != null && !StringUtil.isNullOrEmpty(authToken.getProxyAuthToken())) {
            transport.setAuthToken(authToken.getProxyAuthToken());
        }
        if (ZimbraLog.soap.isDebugEnabled()) {
            ZimbraLog.soap.debug("Proxying request: proxy=%s targetAcctId=%s", toString(), zsc.getRequestedAccountId());
        }
        disableCsrfFlagInAuthToken(envelope, authToken, request.getQName());
        Element response = transport.invokeRaw(envelope);
        Element body = transport.extractBodyElement(response);
        return new Pair<Element, Element>(transport.getZimbraContext(), body);
    } catch (IOException e) {
        throw ServiceException.PROXY_ERROR(e, mURL);
    } finally {
        if (transport != null)
            transport.shutdown();
    }
}
Also used : Element(com.zimbra.common.soap.Element) SoapProtocol(com.zimbra.common.soap.SoapProtocol) AuthToken(com.zimbra.cs.account.AuthToken) SoapHttpTransport(com.zimbra.common.soap.SoapHttpTransport) IOException(java.io.IOException) Pair(com.zimbra.common.util.Pair)

Example 15 with SoapProtocol

use of com.zimbra.common.soap.SoapProtocol in project zm-mailbox by Zimbra.

the class TestCsrfRequest method getCreateSigWithAuthAndCsrfDisabled.

@Test
public void getCreateSigWithAuthAndCsrfDisabled() throws Exception {
    Account acct = provUtil.createAccount(genAcctNameLocalPart(), domain);
    boolean csrfEnabled = Boolean.FALSE;
    SoapTransport transport = authUser(acct.getName(), csrfEnabled, Boolean.FALSE);
    String sigContent = "xss&lt;script&gt;alert(\"XSS\")&lt;/script&gt;&lt;a href=javascript:alert(\"XSS\")&gt;&lt;";
    Signature sig = new Signature(null, "testSig", sigContent, "text/html");
    CreateSignatureRequest req = new CreateSignatureRequest(sig);
    SoapProtocol proto = SoapProtocol.Soap12;
    Element sigReq = JaxbUtil.jaxbToElement(req, proto.getFactory());
    try {
        Element element = transport.invoke(sigReq, false, false, null);
        String sigt = element.getElement("signature").getAttribute("id");
        assertNotNull(sigt);
    } catch (SoapFaultException e) {
        e.printStackTrace();
        assertNull(e);
    }
}
Also used : Account(com.zimbra.cs.account.Account) CreateSignatureRequest(com.zimbra.soap.account.message.CreateSignatureRequest) Signature(com.zimbra.soap.account.type.Signature) Element(com.zimbra.common.soap.Element) SoapProtocol(com.zimbra.common.soap.SoapProtocol) SoapTransport(com.zimbra.common.soap.SoapTransport) SoapFaultException(com.zimbra.common.soap.SoapFaultException) Test(org.junit.Test)

Aggregations

SoapProtocol (com.zimbra.common.soap.SoapProtocol)22 Element (com.zimbra.common.soap.Element)18 SoapFaultException (com.zimbra.common.soap.SoapFaultException)11 Account (com.zimbra.cs.account.Account)11 SoapTransport (com.zimbra.common.soap.SoapTransport)9 CreateSignatureRequest (com.zimbra.soap.account.message.CreateSignatureRequest)9 Signature (com.zimbra.soap.account.type.Signature)9 Test (org.junit.Test)9 ServiceException (com.zimbra.common.service.ServiceException)3 AuthToken (com.zimbra.cs.account.AuthToken)3 AdminDocumentHandler (com.zimbra.cs.service.admin.AdminDocumentHandler)3 IOException (java.io.IOException)3 HashMap (java.util.HashMap)3 SoapHttpTransport (com.zimbra.common.soap.SoapHttpTransport)2 AccountServiceException (com.zimbra.cs.account.AccountServiceException)2 AuthFailedServiceException (com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException)2 Provisioning (com.zimbra.cs.account.Provisioning)2 Mailbox (com.zimbra.cs.mailbox.Mailbox)2 GetSignaturesRequest (com.zimbra.soap.account.message.GetSignaturesRequest)2 HttpServletRequest (javax.servlet.http.HttpServletRequest)2