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 Mailbox method processICalReplies.
private void processICalReplies(OperationContext octxt, ZVCalendar cal, String sender) throws ServiceException {
// Reply from Outlook will usually have PRODID set to the following:
//
// Outlook2007+ZCO: PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN
// Outlook2010+ZCO: PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN
// Outlook20xx+Exchange: PRODID:Microsoft Exchange Server 2007
// (if Exchange is Exchange 2007; Exchange 2010 probably works similarly)
//
// Lowest common denominator is "Microsoft" substring.
String prodId = cal.getPropVal(ICalTok.PRODID, null);
boolean fromOutlook = prodId != null && prodId.toLowerCase().contains("microsoft");
AccountAddressMatcher acctMatcher = new AccountAddressMatcher(getAccount());
List<Invite> components = Invite.createFromCalendar(getAccount(), null, cal, false);
for (Invite inv : components) {
String orgAddress;
if (inv.hasOrganizer()) {
ZOrganizer org = inv.getOrganizer();
orgAddress = org.getAddress();
} else {
ZimbraLog.calendar.warn("No ORGANIZER found in REPLY. Assuming current mailbox.");
orgAddress = getAccount().getName();
}
if (acctMatcher.matches(orgAddress)) {
// RECURRENCE-ID.
if (fromOutlook && !inv.isAllDayEvent() && inv.hasRecurId()) {
RecurId rid = inv.getRecurId();
if (rid.getDt() != null && rid.getDt().hasZeroTime()) {
CalendarItem calItem = getCalendarItemByUid(octxt, inv.getUid());
if (calItem != null) {
Invite seriesInv = calItem.getDefaultInviteOrNull();
if (seriesInv != null) {
ParsedDateTime seriesDtStart = seriesInv.getStartTime();
if (seriesDtStart != null) {
ParsedDateTime fixedDt = seriesDtStart.cloneWithNewDate(rid.getDt());
RecurId fixedRid = new RecurId(fixedDt, rid.getRange());
ZimbraLog.calendar.debug("Fixed up invalid RECURRENCE-ID with zero time; before=[%s], after=[%s]", rid, fixedRid);
inv.setRecurId(fixedRid);
}
}
}
}
}
processICalReply(octxt, inv, sender);
} else {
Account orgAccount = inv.getOrganizerAccount();
// Unknown organizer
if (orgAccount == null) {
ZimbraLog.calendar.warn("Unknown organizer " + orgAddress + " in REPLY");
continue;
}
if (Provisioning.onLocalServer(orgAccount)) {
// Run in the context of organizer's mailbox.
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(orgAccount);
OperationContext orgOctxt = new OperationContext(mbox);
mbox.processICalReply(orgOctxt, inv, sender);
} else {
// Organizer's mailbox is on a remote server.
String uri = AccountUtil.getSoapUri(orgAccount);
if (uri == null) {
ZimbraLog.calendar.warn("Unable to determine URI for organizer account %s", orgAddress);
continue;
}
try {
// TODO: Get the iCalendar data from the
// MIME part since we already have it.
String ical;
StringWriter sr = null;
try {
sr = new StringWriter();
inv.setMethod(ICalTok.REPLY.toString());
inv.newToICalendar(true).toICalendar(sr);
ical = sr.toString();
} finally {
if (sr != null) {
sr.close();
}
}
Options options = new Options();
AuthToken authToken = AuthToken.getCsrfUnsecuredAuthToken(getAuthToken(octxt));
options.setAuthToken(authToken.toZAuthToken());
options.setTargetAccount(orgAccount.getName());
options.setTargetAccountBy(AccountBy.name);
options.setUri(uri);
options.setNoSession(true);
ZMailbox zmbox = ZMailbox.getMailbox(options);
zmbox.iCalReply(ical, sender);
} catch (IOException e) {
throw ServiceException.FAILURE("Error while posting REPLY to organizer mailbox host", e);
}
}
}
}
}
use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class CsrfUtil method main.
public static void main(String[] args) {
try {
AuthToken at = ZimbraAuthToken.getAuthToken("0_f66f9e23c3d6ec89c0723375489c729b13b108d9_69643d33363a34313537336365352d303035352d343066362d626235372d6264396238663136663666393b6578703d31333a313430333935303235363538323b747970653d363a7a696d6272613b7469643d31303a313837363638363831333b76657273696f6e3d303a3b637372663d313a313b");
String csrfToken = "0_a00d6f6af20bf183ab63911ab648a7869793158e";
boolean result = CsrfUtil.isValidCsrfToken(csrfToken, at);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class SoapEngine method dispatchRequest.
/**
* Handles individual requests, either direct or from a batch
*/
Element dispatchRequest(DocumentHandler handler, Element soapReqElem, Map<String, Object> context, ZimbraSoapContext zsc) {
long startTime = System.currentTimeMillis();
SoapProtocol soapProto = zsc.getResponseProtocol();
if (soapReqElem == null) {
return soapFault(soapProto, "cannot dispatch request", ServiceException.INVALID_REQUEST("no document specified", null));
}
if (handler == null) {
return soapFault(soapProto, "cannot dispatch request", ServiceException.UNKNOWN_DOCUMENT(soapReqElem.getQualifiedName(), null));
}
if (RedoLogProvider.getInstance().isSlave() && !handler.isReadOnly()) {
return soapFault(soapProto, "cannot dispatch request", ServiceException.NON_READONLY_OPERATION_DENIED());
}
AuthToken at = zsc.getAuthToken();
boolean needsAuth = handler.needsAuth(context);
boolean needsAdminAuth = handler.needsAdminAuth(context);
if ((needsAuth || needsAdminAuth) && at == null) {
return soapFault(soapProto, "cannot dispatch request", ServiceException.AUTH_REQUIRED());
}
Element response = null;
SoapTransport.setVia(zsc.getNextVia());
try {
Provisioning prov = Provisioning.getInstance();
if (!prov.getLocalServer().getBooleanAttr(Provisioning.A_zimbraUserServicesEnabled, true) && !(handler instanceof AdminDocumentHandler)) {
return soapFault(soapProto, "cannot dispatch request", ServiceException.TEMPORARILY_UNAVAILABLE());
}
if (needsAdminAuth) {
AdminAccessControl aac = AdminAccessControl.getAdminAccessControl(at);
if (!aac.isSufficientAdminForSoap(context, handler)) {
return soapFault(soapProto, "cannot dispatch request", ServiceException.PERM_DENIED("need adequate admin token"));
}
}
String acctId = null;
boolean isGuestAccount = true;
boolean delegatedAuth = false;
if (at != null) {
acctId = at.getAccountId();
isGuestAccount = acctId.equals(GuestAccount.GUID_PUBLIC);
delegatedAuth = at.isDelegatedAuth();
}
if (!isGuestAccount) {
Account acct = null;
if (needsAuth || needsAdminAuth) {
try {
acct = AuthProvider.validateAuthToken(prov, at, false);
} catch (ServiceException e) {
return soapFault(soapProto, null, e);
}
// also, make sure that the target account (if any) is active
if (zsc.isDelegatedRequest() && !handler.isAdminCommand()) {
Account target = DocumentHandler.getRequestedAccount(zsc);
// treat the account as inactive if (a) it doesn't exist, (b) it's in maintenance mode, or (c) we're non-admins and it's not "active"
boolean inactive = target == null || Provisioning.ACCOUNT_STATUS_MAINTENANCE.equals(target.getAccountStatus(prov));
if (!inactive && (!at.isAdmin() || !AccessManager.getInstance().canAccessAccount(at, target))) {
inactive = !target.getAccountStatus(prov).equals(Provisioning.ACCOUNT_STATUS_ACTIVE);
}
if (inactive) {
return soapFault(soapProto, "target account is not active", AccountServiceException.ACCOUNT_INACTIVE(target == null ? zsc.getRequestedAccountId() : target.getName()));
}
}
}
// fault in a session for this handler (if necessary) before executing the command
context.put(ZIMBRA_SESSION, handler.getSession(zsc));
// try to proxy the request if necessary (don't proxy commands that don't require auth)
if ((needsAuth || needsAdminAuth) && acct != null) {
response = handler.proxyIfNecessary(soapReqElem, context);
}
}
// if no proxy, execute the request locally
if (response == null) {
if (delegatedAuth) {
handler.logAuditAccess(at.getAdminAccountId(), acctId, acctId);
}
response = handler.handle(soapReqElem, context);
ZimbraPerf.SOAP_TRACKER.addStat(getStatName(soapReqElem), startTime);
long duration = System.currentTimeMillis() - startTime;
if (LC.zimbra_slow_logging_enabled.booleanValue() && duration > LC.zimbra_slow_logging_threshold.longValue() && !soapReqElem.getQName().getName().equals(MailConstants.SYNC_REQUEST.getName())) {
ZimbraLog.soap.warn("Slow SOAP request (start=" + startTime + "):\n" + soapReqElem.prettyPrint(true));
ZimbraLog.soap.warn("Slow SOAP response (time=" + duration + "):\n" + response.prettyPrint());
}
}
} catch (SoapFaultException e) {
response = e.getFault() != null ? e.getFault().detach() : soapProto.soapFault(ServiceException.FAILURE(e.toString(), e));
if (!e.isSourceLocal()) {
LOG.debug("handler exception", e);
}
} catch (AuthFailedServiceException e) {
HttpServletRequest httpReq = (HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
httpReq.setAttribute(ZimbraInvalidLoginFilter.AUTH_FAILED, Boolean.TRUE);
String clientIp = (String) context.get(SoapEngine.REQUEST_IP);
httpReq.setAttribute(SoapEngine.REQUEST_IP, clientIp);
response = soapProto.soapFault(e);
if (LOG.isDebugEnabled()) {
LOG.debug("handler exception: %s%s", e.getMessage(), e.getReason(", %s"), e);
} else {
// Don't log stack trace for auth failures, since they commonly happen
LOG.info("handler exception: %s%s", e.getMessage(), e.getReason(", %s"));
}
} catch (ServiceException e) {
response = soapFault(soapProto, "handler exception", e);
// XXX: if the session was new, do we want to delete it?
} catch (Throwable e) {
// don't interfere with Jetty Continuations -- pass the exception on up
if (e.getClass().getName().equals("org.eclipse.jetty.continuation.ContinuationThrowable")) {
throw (Error) e;
}
// TODO: better exception stack traces during develope?
response = soapProto.soapFault(ServiceException.FAILURE(e.toString(), e));
if (e instanceof OutOfMemoryError) {
Zimbra.halt("handler exception", e);
}
LOG.warn("handler exception", e);
// XXX: if the session was new, do we want to delete it?
} finally {
SoapTransport.clearVia();
}
return response;
}
use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.
the class DocumentHandler method proxyRequest.
protected Element proxyRequest(Element request, Map<String, Object> context, Server server, ZimbraSoapContext zsc) throws ServiceException {
// figure out whether we can just re-dispatch or if we need to proxy via HTTP
SoapEngine engine = (SoapEngine) context.get(SoapEngine.ZIMBRA_ENGINE);
boolean isLocal = getLocalHostId().equalsIgnoreCase(server.getId());
//reset proxy token if proxying locally; it could previously be set to wrong account
if (isLocal) {
zsc.resetProxyAuthToken();
}
//make sure proxy token is set correctly for current requested acct
if (zsc.getRequestedAccountId() != null) {
try {
AuthToken at = zsc.getAuthToken();
String proxyToken = getProxyAuthToken(zsc.getRequestedAccountId(), context);
if (at != null && (at.getProxyAuthToken() == null || !at.getProxyAuthToken().equals(proxyToken))) {
at.setProxyAuthToken(proxyToken);
}
} catch (ServiceException se) {
ZimbraLog.soap.warn("failed to set proxy auth token", se);
}
}
Element response = null;
request.detach();
if (isLocal && engine != null) {
// executing on same server; just hand back to the SoapEngine
Map<String, Object> contextTarget = new HashMap<String, Object>(context);
contextTarget.put(SoapEngine.ZIMBRA_ENGINE, engine);
contextTarget.put(SoapEngine.ZIMBRA_CONTEXT, zsc);
if (ZimbraLog.soap.isDebugEnabled()) {
ZimbraLog.soap.debug("Proxying request locally: targetServer=%s (id=%s) localHost=%s (id=%s)", server.getName(), server.getId(), LOCAL_HOST, LOCAL_HOST_ID);
}
response = engine.dispatchRequest(request, contextTarget, zsc);
if (zsc.getResponseProtocol().isFault(response)) {
zsc.getResponseProtocol().updateArgumentsForRemoteFault(response, zsc.getRequestedAccountId());
throw new SoapFaultException("error in proxied request", true, response);
}
} else {
// do any necessary operations before doing a cross-server proxy
preProxy(request, context);
// executing remotely; find our target and proxy there
HttpServletRequest httpreq = (HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
ProxyTarget proxy = new ProxyTarget(server.getId(), zsc.getAuthToken(), httpreq);
if (proxyTimeout >= 0) {
proxy.setTimeouts(proxyTimeout);
}
response = proxyWithNotification(request, proxy, zsc, (Session) context.get(SoapEngine.ZIMBRA_SESSION));
// do any necessary operations after doing a cross-server proxy
postProxy(request, response, context);
}
return response;
}
Aggregations