use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class AuthProvider method getAuthToken.
public static AuthToken getAuthToken(Element authTokenElem, Account acct) throws AuthTokenException {
AuthToken at = null;
List<AuthProvider> providers = getProviders();
AuthTokenException authTokenExp = null;
for (AuthProvider ap : providers) {
try {
at = ap.authToken(authTokenElem, acct);
if (at == null) {
authTokenExp = new AuthTokenException("auth provider " + ap.getName() + " returned null");
} else {
return at;
}
} catch (AuthProviderException e) {
// if there is no auth data for this provider, log and continue with next provider
if (e.canIgnore()) {
logger().debug(ap.getName() + ":" + e.getMessage());
} else {
authTokenExp = new AuthTokenException("auth provider error", e);
}
} catch (AuthTokenException e) {
//log and store exception reference
authTokenExp = e;
logger().debug("getAuthToken error: provider=" + ap.getName() + ", err=" + e.getMessage(), e);
}
}
//If multiple auth providers caused AuthTokenException, then last exception is rethrown from here.
if (null != authTokenExp) {
throw authTokenExp;
}
// there is no auth data for any of the enabled providers
return null;
}
use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class AuthProvider method getAuthToken.
/**
* The static getAuthToken methods go through all the providers, trying them in order
* until one returns an AuthToken.
*
* If any provider in the chain throws AuthTokenException,
* it will be stored and re-thrown to caller at the end.
*
* If more than one provider throws AuthTokenException then exception reported
* by last provider will be thrown to caller.
*
* If AuthProviderException is thrown by provider then-
* - For AuthProviderException that is ignorable(AuthProviderException.NO_AUTH_TOKEN, AuthProviderException.NOT_SUPPORTED),
* it will be logged and next provider will be tried.
* - For AuthProviderExceptions that is not ignorable, AuthTokenException is generated and stored,
* thrown at the end if all provider fails.
*
* Return null when all providers fails to get AuthToken and no exception thrown by any provider.
*/
/**
* @param req http request
* @return an AuthToken object, or null if auth data is not present for any of the enabled providers
* @throws ServiceException
*/
public static AuthToken getAuthToken(HttpServletRequest req, boolean isAdminReq) throws AuthTokenException {
AuthToken at = null;
List<AuthProvider> providers = getProviders();
AuthTokenException authTokenExp = null;
for (AuthProvider ap : providers) {
try {
at = ap.authToken(req, isAdminReq);
if (at == null) {
authTokenExp = new AuthTokenException("auth provider " + ap.getName() + " returned null");
} else {
return at;
}
} catch (AuthProviderException e) {
// if there is no auth data for this provider, log and continue with next provider
if (e.canIgnore()) {
logger().debug(ap.getName() + ":" + e.getMessage());
} else {
authTokenExp = new AuthTokenException("auth provider error", e);
}
} catch (AuthTokenException e) {
//log and store exception reference
authTokenExp = e;
logger().debug("getAuthToken error: provider=" + ap.getName() + ", err=" + e.getMessage(), e);
}
}
//If multiple auth providers caused AuthTokenException, then last exception is rethrown from here.
if (null != authTokenExp) {
throw authTokenExp;
}
// there is no auth data for any of the enabled providers
return null;
}
use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class ProxyServlet method doProxy.
private void doProxy(HttpServletRequest req, HttpServletResponse resp) throws IOException {
ZimbraLog.clearContext();
boolean isAdmin = isAdminRequest(req);
AuthToken authToken = isAdmin ? getAdminAuthTokenFromCookie(req, resp, true) : getAuthTokenFromCookie(req, resp, true);
if (authToken == null) {
String zAuthToken = req.getParameter(QP_ZAUTHTOKEN);
if (zAuthToken != null) {
try {
authToken = AuthProvider.getAuthToken(zAuthToken);
if (authToken.isExpired()) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authtoken expired");
return;
}
if (!authToken.isRegistered()) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authtoken is invalid");
return;
}
if (isAdmin && !authToken.isAdmin()) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "permission denied");
return;
}
} catch (AuthTokenException e) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "unable to parse authtoken");
return;
}
}
}
if (authToken == null) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "no authtoken cookie");
return;
}
// get the posted body before the server read and parse them.
byte[] body = copyPostedData(req);
// sanity check
String target = req.getParameter(TARGET_PARAM);
if (target == null) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// check for permission
URL url = new URL(target);
if (!isAdmin && !checkPermissionOnTarget(url, authToken)) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
// determine whether to return the target inline or store it as an upload
String uploadParam = req.getParameter(UPLOAD_PARAM);
boolean asUpload = uploadParam != null && (uploadParam.equals("1") || uploadParam.equalsIgnoreCase("true"));
HttpMethod method = null;
try {
HttpClient client = ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient();
HttpProxyUtil.configureProxy(client);
String reqMethod = req.getMethod();
if (reqMethod.equalsIgnoreCase("GET")) {
method = new GetMethod(target);
} else if (reqMethod.equalsIgnoreCase("POST")) {
PostMethod post = new PostMethod(target);
if (body != null)
post.setRequestEntity(new ByteArrayRequestEntity(body, req.getContentType()));
method = post;
} else if (reqMethod.equalsIgnoreCase("PUT")) {
PutMethod put = new PutMethod(target);
if (body != null)
put.setRequestEntity(new ByteArrayRequestEntity(body, req.getContentType()));
method = put;
} else if (reqMethod.equalsIgnoreCase("DELETE")) {
method = new DeleteMethod(target);
} else {
ZimbraLog.zimlet.info("unsupported request method: " + reqMethod);
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// handle basic auth
String auth, user, pass;
auth = req.getParameter(AUTH_PARAM);
user = req.getParameter(USER_PARAM);
pass = req.getParameter(PASS_PARAM);
if (auth != null && user != null && pass != null) {
if (!auth.equals(AUTH_BASIC)) {
ZimbraLog.zimlet.info("unsupported auth type: " + auth);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
HttpState state = new HttpState();
state.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, pass));
client.setState(state);
method.setDoAuthentication(true);
}
Enumeration headers = req.getHeaderNames();
while (headers.hasMoreElements()) {
String hdr = (String) headers.nextElement();
ZimbraLog.zimlet.debug("incoming: " + hdr + ": " + req.getHeader(hdr));
if (canProxyHeader(hdr)) {
ZimbraLog.zimlet.debug("outgoing: " + hdr + ": " + req.getHeader(hdr));
if (hdr.equalsIgnoreCase("x-host"))
method.getParams().setVirtualHost(req.getHeader(hdr));
else
method.addRequestHeader(hdr, req.getHeader(hdr));
}
}
try {
if (!(reqMethod.equalsIgnoreCase("POST") || reqMethod.equalsIgnoreCase("PUT"))) {
method.setFollowRedirects(true);
}
HttpClientUtil.executeMethod(client, method);
} catch (HttpException ex) {
ZimbraLog.zimlet.info("exception while proxying " + target, ex);
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
int status = method.getStatusLine() == null ? HttpServletResponse.SC_INTERNAL_SERVER_ERROR : method.getStatusCode();
// workaround for Alexa Thumbnails paid web service, which doesn't bother to return a content-type line
Header ctHeader = method.getResponseHeader("Content-Type");
String contentType = ctHeader == null || ctHeader.getValue() == null ? DEFAULT_CTYPE : ctHeader.getValue();
InputStream targetResponseBody = method.getResponseBodyAsStream();
if (asUpload) {
String filename = req.getParameter(FILENAME_PARAM);
if (filename == null || filename.equals(""))
filename = new ContentType(contentType).getParameter("name");
if ((filename == null || filename.equals("")) && method.getResponseHeader("Content-Disposition") != null)
filename = new ContentDisposition(method.getResponseHeader("Content-Disposition").getValue()).getParameter("filename");
if (filename == null || filename.equals(""))
filename = "unknown";
List<Upload> uploads = null;
if (targetResponseBody != null) {
try {
Upload up = FileUploadServlet.saveUpload(targetResponseBody, filename, contentType, authToken.getAccountId());
uploads = Arrays.asList(up);
} catch (ServiceException e) {
if (e.getCode().equals(MailServiceException.UPLOAD_REJECTED))
status = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE;
else
status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
}
resp.setStatus(status);
FileUploadServlet.sendResponse(resp, status, req.getParameter(FORMAT_PARAM), null, uploads, null);
} else {
resp.setStatus(status);
resp.setContentType(contentType);
for (Header h : method.getResponseHeaders()) if (canProxyHeader(h.getName()))
resp.addHeader(h.getName(), h.getValue());
if (targetResponseBody != null)
ByteUtil.copy(targetResponseBody, true, resp.getOutputStream(), true);
}
} finally {
if (method != null)
method.releaseConnection();
}
}
use of com.zimbra.cs.account.AuthToken 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);
}
use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class DocumentHandler method getOperationContext.
public static OperationContext getOperationContext(ZimbraSoapContext zsc, Session session) throws ServiceException {
AuthToken at = zsc.getAuthToken();
OperationContext octxt = new OperationContext(at);
octxt.setChangeConstraint(zsc.getChangeConstraintType(), zsc.getChangeConstraintLimit());
octxt.setRequestIP(zsc.getRequestIP()).setSession(session);
octxt.setUserAgent(zsc.getUserAgent());
octxt.setmResponseProtocol(zsc.getmResponseProtocol());
octxt.setmRequestedAccountId(zsc.getRequestedAccountId());
octxt.setmAuthTokenAccountId(zsc.getAuthtokenAccountId());
return octxt;
}
Aggregations