Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZCS-13176 : Add mail recall message verification header #1468

Open
wants to merge 1 commit into
base: feature/mailRecall_ZCS-12409
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7221,6 +7221,14 @@ public static TwoFactorAuthSecretEncoding fromString(String s) throws ServiceExc
@ZAttr(id=4094)
public static final String A_zimbraFeatureMailRecallEnabled = "zimbraFeatureMailRecallEnabled";

/**
* Secret key used in Mail Recall to make it more secure from spoof.
*
* @since ZCS 10.1.5
*/
@ZAttr(id=4136)
public static final String A_zimbraFeatureMailRecallSecretKey = "zimbraFeatureMailRecallSecretKey";

/**
* Time(in minutes) within which a message can be recalled. The default
* time is 30 minutes and accepts value from 1 to 30.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class ServiceException extends Exception {
public static final String UNSUPPORTED = "service.UNSUPPORTED";
public static final String FORBIDDEN = "service.FORBIDDEN";
public static final String SIEVE_SCRIPT_MAX_SIZE_EXCEPTION = "service.SIEVE_SCRIPT_MAX_SIZE_EXCEPTION";
public static final String MAIL_RECALL_ERROR = "service.MAIL_RECALL_ERROR";

// generic "not found" error for objects other than mail items
public static final String NOT_FOUND = "service.NOT_FOUND";
Expand Down Expand Up @@ -303,6 +304,10 @@ public static ServiceException LICENSE_ERROR(String message, Throwable cause) {
return new ServiceException("license error: "+message, LICENSE_ERROR, RECEIVERS_FAULT, cause);
}

public static ServiceException ERROR_MESSAGE(String str, Throwable cause){
return new ServiceException(String.format("mailRecall error: %s", str), MAIL_RECALL_ERROR, SENDERS_FAULT, cause);
}

public static ServiceException ERROR_WHILE_PARSING_UPLOAD(String message, Throwable cause) {
return new ServiceException(
String.format("ioexception during upload: %s", message), ERROR_WHILE_PARSING_UPLOAD, RECEIVERS_FAULT, cause);
Expand Down
6 changes: 6 additions & 0 deletions common/src/java/com/zimbra/common/soap/AdminConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -1643,6 +1643,12 @@ public final class AdminConstants {
public static final QName VALIDATE_S3_BUCKET_REACHABLE_REQUEST = QName.get(E_VALIDATE_S3_BUCKET_REACHABLE_REQUEST, NAMESPACE);
public static final QName VALIDATE_S3_BUCKET_REACHABLE_RESPONSE = QName.get(E_VALIDATE_S3_BUCKET_REACHABLE_RESPONSE, NAMESPACE);

// Secret key for mail recall
public static final String E_GENERATE_SECRET_KEY_REQUEST = "GenerateSecretKeyRequest";
public static final String E_GENERATE_SECRET_KEY_RESPONSE = "GenerateSecretKeyResponse";
public static final QName GENERATE_SECRET_KEY_REQUEST = QName.get(E_GENERATE_SECRET_KEY_REQUEST, NAMESPACE);
public static final QName GENERATE_SECRET_KEY_RESPONSE = QName.get(E_GENERATE_SECRET_KEY_RESPONSE, NAMESPACE);

// Removed Zetras zimlet package list
public static final List<String> ZEXTRAS_PACKAGES_LIST = Arrays.asList("com_ng_auth", "com_zextras_zextras",
"com_zextras_client", "com_zimbra_connect_classic", "com_zimbra_connect_modern", "com_zextras_docs",
Expand Down
7 changes: 7 additions & 0 deletions common/src/java/com/zimbra/common/soap/MailConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -1425,4 +1425,11 @@ private MailConstants() {
public static final QName RECOVER_ACCOUNT_RESPONSE = QName.get(E_RECOVER_ACCOUNT_RESPONSE, NAMESPACE);
public static final String A_RECOVERY_ACCOUNT = "recoveryAccount";
public static final String A_CHANNEL = "channel";

// MailRecall Uses
public static final String E_MAIL_RECALL_REQUEST = "MailRecallRequest";
public static final String E_MAIL_RECALL_RESPONSE = "MailRecallResponse";
public static final QName MAIL_RECALL_REQUEST = QName.get(E_MAIL_RECALL_REQUEST, NAMESPACE);
public static final QName MAIL_RECALL_RESPONSE = QName.get(E_MAIL_RECALL_RESPONSE, NAMESPACE);
public static final String ITEM_ID = "itemId";
}
10 changes: 8 additions & 2 deletions soap/src/java/com/zimbra/soap/JaxbUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package com.zimbra.soap;

import com.zimbra.soap.mail.message.MailRecallRequest;
import com.zimbra.soap.mail.message.MailRecallResponse;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Iterator;
Expand Down Expand Up @@ -1084,6 +1086,8 @@ public final class JaxbUtil {
com.zimbra.soap.mail.message.VerifyCodeResponse.class,
com.zimbra.soap.mail.message.WaitSetRequest.class,
com.zimbra.soap.mail.message.WaitSetResponse.class,
com.zimbra.soap.mail.message.MailRecallRequest.class,
com.zimbra.soap.mail.message.MailRecallResponse.class,
com.zimbra.soap.replication.message.BecomeMasterRequest.class,
com.zimbra.soap.replication.message.BecomeMasterResponse.class,
com.zimbra.soap.replication.message.BringDownServiceIPRequest.class,
Expand Down Expand Up @@ -1171,7 +1175,9 @@ public final class JaxbUtil {
com.zimbra.soap.admin.message.ValidateS3BucketReachableRequest.class,
com.zimbra.soap.admin.message.ValidateS3BucketReachableResponse.class,
com.zimbra.soap.admin.message.EditS3BucketConfigRequest.class,
com.zimbra.soap.admin.message.EditS3BucketConfigResponse.class
com.zimbra.soap.admin.message.EditS3BucketConfigResponse.class,
com.zimbra.soap.admin.message.GenerateSecretKeyRequest.class,
com.zimbra.soap.admin.message.GenerateSecretKeyResponse.class
};

try {
Expand Down Expand Up @@ -1681,7 +1687,7 @@ public static ModifyItemNotification getModifiedItemSOAP(BaseItemInfo mod, int r
ImapMessageInfo messageInfo = new ImapMessageInfo(mod.getIdInMailbox(), mod.getImapUid(), mod.getMailItemType().toString(), mod.getFlagBitmask(), tags);
return new ModifyNotification.ModifyItemNotification(messageInfo, reason);
}
shrutig0510 marked this conversation as resolved.
Show resolved Hide resolved

public static DeleteItemNotification getDeletedItemSOAP(int itemId, String type) throws ServiceException {
return new DeleteItemNotification(itemId, type);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2025 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.soap.admin.message;

import com.zimbra.common.soap.AdminConstants;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
* @zm-api-command-auth-required true
* @zm-api-command-admin-auth-required true
* @zm-api-command-description Create random secret key and update it to LDAP attribute.
*/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = AdminConstants.E_GENERATE_SECRET_KEY_REQUEST)
@XmlType(propOrder = {})
public class GenerateSecretKeyRequest {

public GenerateSecretKeyRequest() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2025 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.soap.admin.message;

import com.zimbra.common.soap.AdminConstants;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = AdminConstants.E_GENERATE_SECRET_KEY_RESPONSE)
@XmlType(propOrder = {})
public class GenerateSecretKeyResponse {

public GenerateSecretKeyResponse() {
}
}
39 changes: 39 additions & 0 deletions soap/src/java/com/zimbra/soap/mail/message/MailRecallRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2025 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.soap.mail.message;

import com.zimbra.common.soap.MailConstants;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = MailConstants.E_MAIL_RECALL_REQUEST)
public class MailRecallRequest {
@XmlAttribute(name = MailConstants.ITEM_ID , required = true)
private int itemId;

public int getItemId() {
return itemId;
}

public void setItemId(int itemId) {
this.itemId = itemId;
}
}
64 changes: 64 additions & 0 deletions soap/src/java/com/zimbra/soap/mail/message/MailRecallResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2025 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.soap.mail.message;

import com.zimbra.common.soap.MailConstants;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = MailConstants.E_MAIL_RECALL_RESPONSE)
public class MailRecallResponse {

@XmlElement(name = "successfulRecall", required = true)
private int successfulRecall;

@XmlElement(name = "unsuccessfulRecall", required = true)
private int unsuccessfulRecall;

@XmlElement(name = "allMailRecalled", required = true)
private boolean allMailRecalled;

// Getters and Setters
public int getSuccessfulRecall() {
return successfulRecall;
}

public void setSuccessfulRecall(int successfulRecall) {
this.successfulRecall = successfulRecall;
}

public int getUnsuccessfulRecall() {
return unsuccessfulRecall;
}

public void setUnsuccessfulRecall(int unsuccessfulRecall) {
this.unsuccessfulRecall = unsuccessfulRecall;
}

public boolean isAllMailRecalled() {
return allMailRecalled;
}

public void setAllMailRecalled(boolean allMailRecalled) {
this.allMailRecalled = allMailRecalled;
}
}

6 changes: 5 additions & 1 deletion store/conf/attrs/zimbra-attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10339,7 +10339,7 @@ TODO: delete them permanently from here
<attr id="4093" name="zimbraTrialConvertAtExpiration" type="boolean" cardinality="single" optionalIn="account,cos,domain" flags="accountCosDomainInherited,accountInfo" since="9.0.0">
<desc>Can be used by an external service to indicate that a trial is set to convert to active at expiration.</desc>
</attr>
<attr id="4094" name="zimbraFeatureMailRecallEnabled" type="boolean" cardinality="single" optionalIn="globalConfig,account,cos,domain" flags="accountInfo,domainInherited,accountCosDomainInherited" since="10.1.0">
<attr id="4094" name="zimbraFeatureMailRecallEnabled" type="boolean" cardinality="single" optionalIn="globalConfig,account,cos,domain" flags="accountInfo,domainInherited,accountCosDomainInherited" callback="GenerateSecretKeyCallback" since="10.1.0">
<globalConfigValue>FALSE</globalConfigValue>
<defaultCOSValue>FALSE</defaultCOSValue>
<desc>Enables the mail recall functionality</desc>
Expand Down Expand Up @@ -10560,4 +10560,8 @@ TODO: delete them permanently from here
<defaultCOSValue>FALSE</defaultCOSValue>
<desc>Feature to enable delivery status notification</desc>
</attr>

<attr id="4136" name="zimbraFeatureMailRecallSecretKey" type="string" cardinality="single" optionalIn="globalConfig" since="10.1.5">
<desc>Secret key used in Mail Recall to make it more secure from spoof.</desc>
</attr>
</attrs>
72 changes: 72 additions & 0 deletions store/src/java/com/zimbra/cs/account/ZAttrConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18823,6 +18823,78 @@ public Map<String,Object> unsetFeatureMailRecallEnabled(Map<String,Object> attrs
return attrs;
}

/**
* Secret key used in Mail Recall to make it more secure from spoof.
*
* @return zimbraFeatureMailRecallSecretKey, or null if unset
*
* @since ZCS 10.1.5
*/
@ZAttr(id=4136)
public String getFeatureMailRecallSecretKey() {
return getAttr(Provisioning.A_zimbraFeatureMailRecallSecretKey, null, true);
}

/**
* Secret key used in Mail Recall to make it more secure from spoof.
*
* @param zimbraFeatureMailRecallSecretKey new value
* @throws com.zimbra.common.service.ServiceException if error during update
*
* @since ZCS 10.1.5
*/
@ZAttr(id=4136)
public void setFeatureMailRecallSecretKey(String zimbraFeatureMailRecallSecretKey) throws com.zimbra.common.service.ServiceException {
HashMap<String,Object> attrs = new HashMap<String,Object>();
mamtadahiwal marked this conversation as resolved.
Show resolved Hide resolved
attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, zimbraFeatureMailRecallSecretKey);
getProvisioning().modifyAttrs(this, attrs);
}

/**
* Secret key used in Mail Recall to make it more secure from spoof.
*
* @param zimbraFeatureMailRecallSecretKey new value
* @param attrs existing map to populate, or null to create a new map
* @return populated map to pass into Provisioning.modifyAttrs
*
* @since ZCS 10.1.5
*/
@ZAttr(id=4136)
public Map<String,Object> setFeatureMailRecallSecretKey(String zimbraFeatureMailRecallSecretKey, Map<String,Object> attrs) {
if (attrs == null) attrs = new HashMap<String,Object>();
mamtadahiwal marked this conversation as resolved.
Show resolved Hide resolved
attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, zimbraFeatureMailRecallSecretKey);
return attrs;
}

/**
* Secret key used in Mail Recall to make it more secure from spoof.
*
* @throws com.zimbra.common.service.ServiceException if error during update
*
* @since ZCS 10.1.5
*/
@ZAttr(id=4136)
public void unsetFeatureMailRecallSecretKey() throws com.zimbra.common.service.ServiceException {
HashMap<String,Object> attrs = new HashMap<String,Object>();
mamtadahiwal marked this conversation as resolved.
Show resolved Hide resolved
attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, "");
getProvisioning().modifyAttrs(this, attrs);
}

/**
* Secret key used in Mail Recall to make it more secure from spoof.
*
* @param attrs existing map to populate, or null to create a new map
* @return populated map to pass into Provisioning.modifyAttrs
*
* @since ZCS 10.1.5
*/
@ZAttr(id=4136)
public Map<String,Object> unsetFeatureMailRecallSecretKey(Map<String,Object> attrs) {
if (attrs == null) attrs = new HashMap<String,Object>();
mamtadahiwal marked this conversation as resolved.
Show resolved Hide resolved
attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, "");
return attrs;
}

/**
* Time(in minutes) within which a message can be recalled. The default
* time is 30 minutes and accepts value from 1 to 30.
Expand Down
Loading