Skip to content

Commit

Permalink
ZCS-16571 Added logic to process soap request only when ldap attribut…
Browse files Browse the repository at this point in the history
…e is enabled
  • Loading branch information
ashwinsahu19 committed Feb 25, 2025
1 parent 71f313a commit e8efc9f
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -803,4 +803,61 @@ public void testNegativeReplaceheaderAction() {
Assert.assertEquals("parse error: Invalid replaceheader action: replaceheader action is not allowed in user scripts", e.getMessage());
}
}

@Test
public void testRedirectFilterActionWhenAttriIsDisabled() {
try {
Account account = Provisioning.getInstance().getAccount(
MockProvisioning.DEFAULT_ACCOUNT_ID);
RuleManager.clearCachedRules(account);
account.setFeatureMailForwardingEnabled(false);
FilterRule rule = new FilterRule("testSetIncomingXMLRules_EmptyAddress", true);

FilterTest.AddressTest test = new FilterTest.AddressTest();
test.setHeader("to");
test.setStringComparison("is");
test.setPart("all");
test.setValue(null);
test.setIndex(0);

FilterTests tests = new FilterTests("anyof");
tests.addTest(test);

FilterAction action = new FilterAction.RedirectAction("[email protected]");
action.setIndex(0);

rule.setFilterTests(tests);
rule.addFilterAction(action);

List<FilterRule> filterRuleList = new ArrayList<FilterRule>();
filterRuleList.add(rule);
RuleManager.setIncomingXMLRules(account, filterRuleList);

String xml = "<ModifyFilterRulesRequest xmlns=\"urn:zimbraMail\">\n" +
" <filterRules>\n" +
" <filterRule name=\"t60\" active=\"1\">\n" +
" <filterTests condition=\"anyof\" index=\"1\">\n" +
" <headerTest stringComparison=\"matches\" header=\"subject\" value=\"*\"/>\n" +
" </filterTests>\n" +
" <filterActions index=\"2\">\n" +
" <actionReplaceheader>\n" +
" <newValue>new_header_value</newValue>\n" +
" <test>\n" +
" <headerName>sub2</headerName>\n" +
" <headerValue>test testing</headerValue>\n" +
" </test>\n" +
" </actionReplaceheader>\n" +
" </filterActions>\n" +
" </filterRule>\n" +
" </filterRules>\n" +
"</ModifyFilterRulesRequest>";


Element request = Element.parseXML(xml);
new ModifyFilterRules().handle(request, ServiceTestUtil.getRequestContext(account));
} catch (Exception e) {
Assert.assertTrue(e instanceof ServiceException);
Assert.assertEquals("feature MailForwardingInFilters is not enabled", e.getMessage());
}
}
}
16 changes: 11 additions & 5 deletions store/src/java/com/zimbra/cs/filter/ZimbraMailAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,12 +397,18 @@ private void executeActionRedirectInternal(Action action) throws ServiceExceptio
} else {
ZimbraLog.filter.info("Redirecting message to %s.", addr);
}
try {
handler.redirect(addr);
} catch (Exception e) {
ZimbraLog.filter.warn("Unable to redirect to %s. Filing message to %s.",
addr, handler.getDefaultFolderPath(), e);
if (!account.isFeatureMailForwardingInFiltersEnabled()) {
ZimbraLog.filter.warn("Redirection to %s is rejected as attribute mailForwarding is disabled. Filing message to %s.",
addr, handler.getDefaultFolderPath());
keep(KeepType.EXPLICIT_KEEP);
} else {
try {
handler.redirect(addr);
} catch (Exception e) {
ZimbraLog.filter.warn("Unable to redirect to %s. Filing message to %s.",
addr, handler.getDefaultFolderPath(), e);
keep(KeepType.EXPLICIT_KEEP);
}
}
}

Expand Down
21 changes: 18 additions & 3 deletions store/src/java/com/zimbra/cs/service/UserServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,22 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Servl
ZimbraLog.addAccountNameToContext(context.getAuthAccount().getName());
}

if (context.targetAccount != null && !context.targetAccount.isFeatureExportFolderEnabled()) {
// classic web app refresh uses export contact with format "contact folder", excluding this format
if( !(context.format == FormatType.CONTACT_FOLDER)) {
throw ServiceException.PERM_DENIED(L10nUtil.getMessage(MsgKey.errPermissionDenied, req));
}
}

doAuthGet(req, resp, context);

if (allowDocumentAccessedTimeLogging(context)) {
logDocumentAccessedTime(context);
}
} catch (ServiceException se) {
if (se.getCode() == ServiceException.PERM_DENIED ||
se instanceof NoSuchItemException) {
if (se.getCode() == ServiceException.PERM_DENIED) {
sendError(context, req, resp, se.getMessage());
} else if (se instanceof NoSuchItemException) {
sendError(context, req, resp, L10nUtil.getMessage(MsgKey.errNoSuchItem, req));
} else if (se.getCode() == ServiceException.AUTH_REQUIRED) {
sendError(context, req, resp, L10nUtil.getMessage(MsgKey.errMustAuthenticate, req));
Expand Down Expand Up @@ -781,6 +789,11 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Serv
}
}
}
if (context.targetAccount != null && !context.targetAccount.isFeatureImportFolderEnabled()) {
// dummy auth successful set for handling the proper error msg in sendError
context.setCsrfAuthSucceeded(true);
throw ServiceException.PERM_DENIED(L10nUtil.getMessage(MsgKey.errPermissionDenied, req));
}
Folder folder = null;
String filename = null;
Mailbox mbox = UserServletUtil.getTargetMailbox(context);
Expand Down Expand Up @@ -857,7 +870,9 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Serv

context.formatter.save(context, ctype, folder, filename);
} catch (ServiceException se) {
if (se.getCode() == ServiceException.PERM_DENIED || se instanceof NoSuchItemException) {
if (se.getCode() == ServiceException.PERM_DENIED) {
sendError(context, req, resp, se.getMessage());
} else if (se instanceof NoSuchItemException) {
sendError(context, req, resp, L10nUtil.getMessage(MsgKey.errNoSuchItem, req));
} else if (se.getCode() == AccountServiceException.MAINTENANCE_MODE
|| se.getCode() == AccountServiceException.ACCOUNT_INACTIVE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ public final class ModifyFilterRules extends MailDocumentHandler {
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Account account = getRequestedAccount(zsc);
String INCOMING_FILTER_RULE = "incomingFilterRule";

if (!canModifyOptions(zsc, account)) {
throw ServiceException.PERM_DENIED("cannot modify options");
}

ModifyFilterRulesRequest req = zsc.elementToJaxb(request);

if (!checkForwardFilterAttr(account, req.getFilterRules(), INCOMING_FILTER_RULE)) {
throw ServiceException.PERM_DENIED("feature MailForwardingInFilters is not enabled");
}

RuleManager.setIncomingXMLRules(account, req.getFilterRules());
return zsc.jaxbToElement(new ModifyFilterRulesResponse());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ public final class ModifyOutgoingFilterRules extends MailDocumentHandler {
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Account account = getRequestedAccount(zsc);
String OUTGOING_FILTER_RULE = "outgoingFilterRule";

if (!canModifyOptions(zsc, account)) {
throw ServiceException.PERM_DENIED("cannot modify options");
}

ModifyOutgoingFilterRulesRequest req = zsc.elementToJaxb(request);

if (!checkForwardFilterAttr(account, req.getFilterRules(), OUTGOING_FILTER_RULE)) {
throw ServiceException.PERM_DENIED("feature MailForwardingInFilters is not enabled");
}

RuleManager.setOutgoingXMLRules(account, req.getFilterRules());
return zsc.jaxbToElement(new ModifyOutgoingFilterRulesResponse());
}
Expand Down
54 changes: 54 additions & 0 deletions store/src/java/com/zimbra/soap/DocumentHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
package com.zimbra.soap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;

import com.zimbra.cs.filter.RuleManager;
import com.zimbra.soap.mail.message.ModifyFilterRulesRequest;
import com.zimbra.soap.mail.message.ModifyOutgoingFilterRulesRequest;
import com.zimbra.soap.mail.type.FilterAction;
import com.zimbra.soap.mail.type.FilterRule;
import org.dom4j.QName;

import com.google.common.annotations.VisibleForTesting;
Expand Down Expand Up @@ -58,6 +65,7 @@ public abstract class DocumentHandler {

private QName responseQName;
private long proxyTimeout = -1;
String INCOMING_FILTER_RULE = "incomingFilterRule";

@VisibleForTesting
public void setResponseQName(QName response) {
Expand Down Expand Up @@ -258,6 +266,52 @@ public boolean canModifyOptions(ZimbraSoapContext zsc, Account acct) throws Serv
}
}

/**
* Return whether creation or modification of filter having mail forwarding instruction
* is permitted or not.
* On basis of attribute zimbraFeatureMailForwardingInFiltersEnabled.
* If zimbraFeatureMailForwardingInFiltersEnabled is disabled, mail forwarding filter creation should be restricted.
*/
public boolean checkForwardFilterAttr(Account account, List<FilterRule> filterRules, String filterRuleType) throws ServiceException {
if (!account.isFeatureMailForwardingInFiltersEnabled() && !filterRules.isEmpty()) {
for (FilterRule filterRule:filterRules) {
if (Objects.requireNonNull(filterRule.getFilterActions()).stream().anyMatch(FilterAction.RedirectAction.class::isInstance) && !checkIfFilterAlreadyExists(filterRule, account, filterRuleType)) {
return false;
}
}
}
return true;
}

public boolean checkIfFilterAlreadyExists(FilterRule filterRule, Account account, String filterRuleType) throws ServiceException {
List<FilterRule> existingFilterRules = INCOMING_FILTER_RULE.equals(filterRuleType) ? RuleManager.getIncomingRulesAsXML(account) : RuleManager.getOutgoingRulesAsXML(account);
for (FilterRule existingFilterRule:existingFilterRules) {
if (filterRule.getName().equals(existingFilterRule.getName()) &&
isFilterActionSame(filterRule, existingFilterRule)) {
return true;
}
}
return false;
}

public boolean isFilterActionSame(FilterRule filterRule, FilterRule existingFilterRule) {
List<FilterAction> filterActions = filterRule.getFilterActions();
List<FilterAction> existingFilterActions = existingFilterRule.getFilterActions();
if (filterActions == null || existingFilterActions == null) {
return false;
}

for (FilterAction filterAction : filterActions) {
if (filterAction instanceof FilterAction.RedirectAction) {
String redirectAddress = ((FilterAction.RedirectAction) filterAction).getAddress();
if (redirectAddress != null && existingFilterActions.stream().filter(action -> action instanceof FilterAction.RedirectAction).noneMatch(action -> redirectAddress.equals(((FilterAction.RedirectAction) action).getAddress()))) {
return false;
}
}
}
return true;
}

/** Returns whether domain admin auth is sufficient to run this command.
* This should be overriden only on admin commands that can be run in a
* restricted "domain admin" mode. */
Expand Down

0 comments on commit e8efc9f

Please sign in to comment.