Add mail sending feature when policy violating

4.x.x
tcdlpds@gmail.com 4 years ago
parent f9bcb808f9
commit 6014592f2c

@ -57,8 +57,10 @@ import javax.validation.constraints.Size;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam; import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -359,4 +361,63 @@ public interface DeviceManagementAdminService {
required = true) required = true)
List<String> deviceIdentifiers); List<String> deviceIdentifiers);
@POST
@Path("/{deviceId}/{featureCode}/")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "Removing Multiple Policies",
notes = "Delete one or more than one policy using this API.",
tags = "Device Policy Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:android:enroll")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully removed the policy."),
@ApiResponse(
code = 400,
message = "Bad Request. \n Invalid request or validation error.",
response = ErrorResponse.class),
@ApiResponse(
code = 404,
message = "Not Found. \n The specified resource does not exist.",
response = ErrorResponse.class),
@ApiResponse(
code = 415,
message = "Unsupported media type. \n The format of the requested entity was not " +
"supported.\n "
+ "supported format."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred whilst bulk removing policies.",
response = ErrorResponse.class)
})
Response triggerCorrectiveActions(
@ApiParam(
name = "deviceId",
value = "Device Identifier.",
required = true)
@PathParam("deviceId")
String deviceId,
@ApiParam(
name = "featureCode",
value = "Policy Feature Code.",
required = true)
@PathParam("featureCode")
String featureCode,
@ApiParam(
name = "actions",
value = "The list of actions to trigger when policy violated.",
required = true)
List<String> actions
);
} }

@ -41,8 +41,15 @@ import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.MDMAppConstants;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService;
import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
@ -56,8 +63,10 @@ import javax.validation.constraints.Size;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam; import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -190,4 +199,38 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
} }
} }
@POST
@Path("/{deviceId}/{featureCode}")
@Override
public Response triggerCorrectiveActions(
@PathParam("deviceId") String deviceIdentifier,
@PathParam("featureCode") String featureCode,
List<String> actions){
DeviceManagementProviderService deviceManagementService = DeviceMgtAPIUtils.getDeviceManagementService();
PlatformConfigurationManagementService platformConfigurationManagementService = DeviceMgtAPIUtils
.getPlatformConfigurationManagementService();
try {
PlatformConfiguration config = platformConfigurationManagementService
.getConfiguration(MDMAppConstants.RegistryConstants.GENERAL_CONFIG_RESOURCE_PATH);
List<ConfigurationEntry> configList = config.getConfiguration();
deviceManagementService.triggerCorrectiveActions(deviceIdentifier, featureCode, actions, configList);
} catch (ConfigurationManagementException e) {
String msg = "Error occurred while processing platform configuration.";
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch (BadRequestException e) {
String msg = "Bad request, can't proceed. Hence verify the request and re-try";
log.error(msg);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (DeviceManagementException e) {
String msg = "Error occurred while getting device data which has ID: " + deviceIdentifier;
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch (DeviceNotFoundException e) {
String msg = "Couldn't find an device for device identifier: " + deviceIdentifier;
log.error(msg);
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
}
return Response.status(Response.Status.OK).entity("Triggered action successfully").build();
}
} }

@ -0,0 +1,60 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.configuration.mgt;
import java.util.List;
public class CorrectiveActionConfig {
private List<String> mailReceivers;
private String mailSubject;
private String mailBody;
private List<String> actionTypes;
public List<String> getMailReceivers() {
return mailReceivers;
}
public void setMailReceivers(List<String> mailReceivers) {
this.mailReceivers = mailReceivers;
}
public String getMailSubject() {
return mailSubject;
}
public void setMailSubject(String mailSubject) {
this.mailSubject = mailSubject;
}
public String getMailBody() {
return mailBody;
}
public void setMailBody(String mailBody) {
this.mailBody = mailBody;
}
public List<String> getActionTypes() {
return actionTypes;
}
public void setActionTypes(List<String> actionTypes) {
this.actionTypes = actionTypes;
}
}

@ -99,6 +99,12 @@ public final class DeviceManagementConstants {
public static final String POLICY_REVOKE_OPERATION_CODE = OperationMgtConstants.OperationCodes.POLICY_REVOKE; public static final String POLICY_REVOKE_OPERATION_CODE = OperationMgtConstants.OperationCodes.POLICY_REVOKE;
} }
public static final class CorrectiveActions {
private CorrectiveActions() {throw new AssertionError();}
public static final String E_MAIL = "MAIL";
}
public static final class EmailAttributes { public static final class EmailAttributes {
private EmailAttributes() { private EmailAttributes() {
throw new AssertionError(); throw new AssertionError();
@ -117,6 +123,7 @@ public final class DeviceManagementConstants {
public static final String USER_REGISTRATION_TEMPLATE = "user-registration"; public static final String USER_REGISTRATION_TEMPLATE = "user-registration";
public static final String USER_ENROLLMENT_TEMPLATE = "user-enrollment"; public static final String USER_ENROLLMENT_TEMPLATE = "user-enrollment";
public static final String USER_VERIFY_TEMPLATE = "user-verify"; public static final String USER_VERIFY_TEMPLATE = "user-verify";
public static final String POLICY_VIOLATE_TEMPLATE = "policy-violating-notifier";
public static final String USER_WELCOME_TEMPLATE = "user-welcome"; public static final String USER_WELCOME_TEMPLATE = "user-welcome";
public static final String DEFAULT_ENROLLMENT_TEMPLATE = "default-enrollment-invitation"; public static final String DEFAULT_ENROLLMENT_TEMPLATE = "default-enrollment-invitation";
} }

@ -47,6 +47,7 @@ import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.StartupOperationConfig; import org.wso2.carbon.device.mgt.common.StartupOperationConfig;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException; import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot; import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
@ -918,4 +919,17 @@ public interface DeviceManagementProviderService {
* @return enrollment steps of each enrollment types which are provided in the device type xml file * @return enrollment steps of each enrollment types which are provided in the device type xml file
*/ */
DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails(String deviceType); DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails(String deviceType);
/**
* This method is called by device when triggered a corrective action
*
* @param deviceIdentifier Device Identifier
* @param featureCode Feature Code
* @param actions Actions
* @param configList Configuration List
* @throws DeviceManagementException if error occurred while triggering corrective action
* @throws DeviceNotFoundException if server doesn't have a device for given device identifier
*/
void triggerCorrectiveActions(String deviceIdentifier, String featureCode, List<String> actions,
List<ConfigurationEntry> configList) throws DeviceManagementException, DeviceNotFoundException;
} }

@ -59,6 +59,7 @@ import org.wso2.carbon.device.mgt.common.DeviceTransferRequest;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.FeatureManager; import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.InitialOperationConfig; import org.wso2.carbon.device.mgt.common.InitialOperationConfig;
import org.wso2.carbon.device.mgt.common.MDMAppConstants;
import org.wso2.carbon.device.mgt.common.MonitoringOperation; import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
@ -69,6 +70,7 @@ import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.AmbiguousConfigurationException; import org.wso2.carbon.device.mgt.common.configuration.mgt.AmbiguousConfigurationException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.CorrectiveActionConfig;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceConfiguration; import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceConfiguration;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo; import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo;
import org.wso2.carbon.device.mgt.common.configuration.mgt.EnrollmentConfiguration; import org.wso2.carbon.device.mgt.common.configuration.mgt.EnrollmentConfiguration;
@ -80,6 +82,7 @@ import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySna
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotificationConfiguration; import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotificationConfiguration;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifier; import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifier;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifierException; import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifierException;
import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
@ -129,6 +132,7 @@ import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementServiceComponent; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementServiceComponent;
import org.wso2.carbon.device.mgt.core.internal.PluginInitializationListener; import org.wso2.carbon.device.mgt.core.internal.PluginInitializationListener;
import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation; import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation;
import org.wso2.carbon.device.mgt.core.report.mgt.Constants;
import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil; import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
import org.wso2.carbon.email.sender.core.ContentProviderInfo; import org.wso2.carbon.email.sender.core.ContentProviderInfo;
import org.wso2.carbon.email.sender.core.EmailContext; import org.wso2.carbon.email.sender.core.EmailContext;
@ -140,6 +144,7 @@ import org.wso2.carbon.stratos.common.beans.TenantInfoBean;
import org.wso2.carbon.tenant.mgt.services.TenantMgtAdminService; import org.wso2.carbon.tenant.mgt.services.TenantMgtAdminService;
import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreException;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller; import javax.xml.bind.Marshaller;
@ -4207,4 +4212,60 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
DeviceManagementService dms = pluginRepository.getDeviceManagementService(deviceType, tenantId); DeviceManagementService dms = pluginRepository.getDeviceManagementService(deviceType, tenantId);
return dms.getDeviceEnrollmentInvitationDetails(); return dms.getDeviceEnrollmentInvitationDetails();
} }
@Override
public void triggerCorrectiveActions(String deviceIdentifier, String featureCode, List<String> actions,
List<ConfigurationEntry> configList) throws DeviceManagementException, DeviceNotFoundException {
if (log.isDebugEnabled()) {
log.debug("Triggering Corrective action. Device Identifier: " + deviceIdentifier);
}
if (configList == null || configList.isEmpty()) {
String msg = "Platform config is not configured";
log.error(msg);
throw new BadRequestException(msg);
}
Device device = getDevice(deviceIdentifier, false);
if (device == null) {
String msg = "Couldn't find and device for device identifier " + deviceIdentifier;
log.error(msg);
throw new DeviceNotFoundException(msg);
}
EnrolmentInfo enrolmentInfo = device.getEnrolmentInfo();
for (String action : actions) {
for (ConfigurationEntry config : configList) {
if (config.getName().equals(featureCode)) {
CorrectiveActionConfig correctiveActionConfig = (CorrectiveActionConfig) config.getValue();
if (correctiveActionConfig.getActionTypes().contains(action)) {
if (DeviceManagementConstants.CorrectiveActions.E_MAIL.equals(action)) {
Properties props = new Properties();
props.setProperty("mail-subject", correctiveActionConfig.getMailSubject());
props.setProperty("feature-code", featureCode);
props.setProperty("device-id", deviceIdentifier);
props.setProperty("device-name", device.getName());
props.setProperty("device-owner", enrolmentInfo.getOwner());
props.setProperty("custom-mail-body", correctiveActionConfig.getMailBody());
try {
for (String mailAddress : correctiveActionConfig.getMailReceivers()) {
EmailMetaInfo metaInfo = new EmailMetaInfo(mailAddress, props);
sendEnrolmentInvitation(
DeviceManagementConstants.EmailAttributes.POLICY_VIOLATE_TEMPLATE,
metaInfo);
}
} catch (ConfigurationManagementException e) {
String msg = "Error occurred while sending the mail.";
log.error(msg);
throw new DeviceManagementException(msg, e);
}
}
} else {
log.warn("Corrective action: " + action + " is not configured in the platform configuration "
+ "for policy " + featureCode);
}
}
}
}
}
} }

@ -37,12 +37,6 @@ import java.util.List;
public interface PolicyManagerService { public interface PolicyManagerService {
/*
Feature addFeature(Feature feature) throws FeatureManagementException;
Feature updateFeature(Feature feature) throws FeatureManagementException;
*/
Profile addProfile(Profile profile) throws PolicyManagementException; Profile addProfile(Profile profile) throws PolicyManagementException;
Profile updateProfile(Profile profile) throws PolicyManagementException; Profile updateProfile(Profile profile) throws PolicyManagementException;
@ -97,9 +91,8 @@ public interface PolicyManagerService {
boolean isCompliant(DeviceIdentifier deviceIdentifier) throws PolicyComplianceException; boolean isCompliant(DeviceIdentifier deviceIdentifier) throws PolicyComplianceException;
PaginationResult getPolicyCompliance( PaginationResult getPolicyCompliance(PaginationRequest paginationRequest, String policyId, boolean complianceStatus,
PaginationRequest paginationRequest, String policyId, boolean complianceStatus, boolean isPending, String fromDate, String toDate) boolean isPending, String fromDate, String toDate) throws PolicyComplianceException;
throws PolicyComplianceException;
List<ComplianceFeature> getNoneComplianceFeatures(int complianceStatusId) throws PolicyComplianceException; List<ComplianceFeature> getNoneComplianceFeatures(int complianceStatusId) throws PolicyComplianceException;
} }

@ -0,0 +1,69 @@
#*
Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*#
<EmailConfig>
<Subject>$mail-subject</Subject>
<Body>
<![CDATA[
<html>
<head>
<title>Entgra IoT Server</title>
</head>
<body style="color: #666666; background-color:#cdcdcd; padding: 0px; margin: 0px;">
<div style="background-color:#cdcdcd; font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; padding: 20px 0px; margin: 0px;">
<div style="width: 86%; max-width: 650px; padding: 2%; background-color: #ffffff; margin: auto; border-radius: 14px;">
<div style="background-color: #ffebcc; line-height: 0px; border-top-left-radius: 10px; border-top-right-radius: 10px; padding: 10px;">
<div style="display: inline-block; line-height: 0px;">
<img alt="entgra" src="https://storage.googleapis.com/cdn-entgra/logo.png" height="50px" width="143px" />
</div>
</div>
<div style="background-color: #ffffff; line-height: 170%; color: #666666; padding: 20px 25px;">
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px 20px;">
Hi,
</p>
<div>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
$feature-code policy is violated in the following device.
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Device Id: </b> $device-id</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Device Name: </b> $device-name</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Device Owner: </b> $device-owner</p>
</div>
<div>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">$custom-mail-body</p>
</div>
<div>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
If you have any further questions, please reach out to us using your registered mail to
<a href="bizdev-group@entgra.io"> bizdev-group@entgra.io.</a> Looking forward to working with you.
</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
Best Regards,
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Entgra Administrator
</p>
</div>
</div>
</div>
</div>
</body>
</html>
]]>
</Body>
</EmailConfig>
Loading…
Cancel
Save