Compare commits

Invalid templates have been ignored

1 invalid template(s) found pull_request_template.md: frontmatter must start with a separator line

...

13 Commits

Author SHA1 Message Date
shamalka 422790ef71 Merge branch 'master' of https://repository.entgra.net/community/device-mgt-core into 201013
12 months ago
shamalka 4238f6ffa1 Add sub tenant reserved user creation
12 months ago
Pahansith Gunathilake e398b8abbd Merge pull request 'Changing the data type of DESCRIPTION from VARCHAR to TEXT' (#295) from arshana790/device-mgt-core-fork:task#10466/change_dbscripts into master
12 months ago
Arshana bd79e4a834 Changing the data type of APP_META_INFO from VARCHAR to TEXT
12 months ago
Lasantha Dharmakeerthi 33005094db Update user-welcome mail template
12 months ago
Arshana 31c79d5bc9 Changing the data type of DESCRIPTION from VARCHAR to TEXT
12 months ago
Rajitha Kumara 6ee86e8cd2 Add java doc comments
12 months ago
Rajitha Kumara 8c0ae2511e Add enrollment mails
12 months ago
Pahansith Gunathilake 63c0606fdf Merge pull request 'Fix app invisibility when retired' (#291) from osh.silva/device-mgt-core:apps-10154 into master
1 year ago
Inosh Perara 924495dc91 Merge pull request 'Add fix for apps not loading in Application Restriction Settings' (#293) from osh.silva/device-mgt-core:app-restrict-10165 into master
1 year ago
osh c1d5ecf0d4 Merge branch 'master' of ssh://repository.entgra.net:222/community/device-mgt-core into app-restrict-10165
1 year ago
osh c4be66bc2e Add fix for apps not loading
1 year ago
osh 1436332b32 Fix app invisibility when retired
1 year ago

@ -84,6 +84,10 @@
<artifactId>okhttp</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.user.api</artifactId>
</dependency>
</dependencies>
<build>
@ -121,7 +125,10 @@
org.wso2.carbon.apimgt.impl;version="${carbon.api.mgt.version.range}",
org.wso2.carbon.apimgt.impl.utils;version="${carbon.api.mgt.version.range}",
org.wso2.carbon.apimgt.impl.internal;version="${carbon.api.mgt.version.range}",
org.json
org.json,
org.wso2.carbon.user.api,
org.wso2.carbon.context;version="4.6",
org.wso2.carbon.utils.*
</Import-Package>
</instructions>
</configuration>
@ -155,4 +162,4 @@
</plugins>
</build>
</project>
</project>

@ -19,6 +19,7 @@
package io.entgra.device.mgt.core.apimgt.extension.rest.api;
import com.google.gson.Gson;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.internal.PublisherRESTAPIDataHolder;
import org.json.JSONObject;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.util.HttpsTrustManagerUtils;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIApplicationKey;
@ -35,7 +36,17 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.apimgt.impl.APIManagerConfiguration;
import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class APIApplicationServicesImpl implements APIApplicationServices {
@ -51,9 +62,27 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
public APIApplicationKey createAndRetrieveApplicationCredentials()
throws APIServicesException {
String serverUser = null;
String serverPassword = null;
try {
UserRealm userRealm = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm();
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
UserStoreManager userStoreManager = userRealm.getUserStoreManager();
createUserIfNotExists(Constants.RESERVED_USER_NAME, Constants.RESERVED_USER_PASSWORD, userStoreManager);
if(tenantDomain.equals("carbon.super")) {
serverUser = config.getFirstProperty(Constants.SERVER_USER);
serverPassword = config.getFirstProperty(Constants.SERVER_PASSWORD);
} else {
serverUser = Constants.RESERVED_USER_NAME + "@" + tenantDomain;
serverPassword = Constants.RESERVED_USER_PASSWORD;
}
} catch (UserStoreException e) {
throw new RuntimeException(e);
}
String applicationEndpoint = config.getFirstProperty(Constants.DCR_END_POINT);
String serverUser = config.getFirstProperty(Constants.SERVER_USER);
String serverPassword = config.getFirstProperty(Constants.SERVER_PASSWORD);
JSONObject jsonObject = new JSONObject();
jsonObject.put("callbackUrl", Constants.EMPTY_STRING);
@ -69,8 +98,9 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
.post(requestBody)
.build();
try {
Response response = client.newCall(request).execute();
return gson.fromJson(response.body().string(), APIApplicationKey.class);
try (Response response = client.newCall(request).execute()) {
return gson.fromJson(response.body().string(), APIApplicationKey.class);
}
} catch (IOException e) {
msg = "Error occurred while processing the response";
log.error(msg, e);
@ -82,8 +112,16 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
public AccessTokenInfo generateAccessTokenFromRegisteredApplication(String consumerKey, String consumerSecret)
throws APIServicesException {
String userName = config.getFirstProperty(Constants.SERVER_USER);
String userPassword = config.getFirstProperty(Constants.SERVER_PASSWORD);
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
String userName = null;
String userPassword = null;
if(tenantDomain.equals("carbon.super")) {
userName = config.getFirstProperty(Constants.SERVER_USER);
userPassword = config.getFirstProperty(Constants.SERVER_PASSWORD);
} else {
userName = "shamalka@shamalka.com";
userPassword = "admin";
}
JSONObject params = new JSONObject();
params.put(Constants.GRANT_TYPE_PARAM_NAME, Constants.PASSWORD_GRANT_TYPE);
@ -125,4 +163,39 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
throw new APIServicesException(e);
}
}
private void createUserIfNotExists(String username, String password, UserStoreManager userStoreManager) {
try {
if (!userStoreManager.isExistingUser(MultitenantUtils.getTenantAwareUsername(username))) {
String[] roles = {"admin"};
userStoreManager.addUser(MultitenantUtils.getTenantAwareUsername(username), password, roles, null, "");
// userStoreManager.updateCredential(MultitenantUtils.getTenantAwareUsername(username), "reservedpwd", password);
}
} catch (UserStoreException e) {
String msg = "Error when trying to fetch tenant details";
log.error(msg);
}
}
private String generateInitialUserPassword() {
int passwordLength = 6;
//defining the pool of characters to be used for initial password generation
String lowerCaseCharset = "abcdefghijklmnopqrstuvwxyz";
String upperCaseCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String numericCharset = "0123456789";
SecureRandom randomGenerator = new SecureRandom();
String totalCharset = lowerCaseCharset + upperCaseCharset + numericCharset;
int totalCharsetLength = totalCharset.length();
StringBuilder initialUserPassword = new StringBuilder();
for (int i = 0; i < passwordLength; i++) {
initialUserPassword.append(
totalCharset.charAt(randomGenerator.nextInt(totalCharsetLength)));
}
if (log.isDebugEnabled()) {
log.debug("Initial user password is created for new user: " + initialUserPassword);
}
return initialUserPassword.toString();
}
}

@ -65,6 +65,8 @@ public final class Constants {
public static final String SCOPE_API_ENDPOINT = "/api/am/publisher/v2/scopes/";
public static final String API_ENDPOINT = "/api/am/publisher/v2/apis/";
public static final String GET_ALL_APIS = "/api/am/publisher/v2/apis?limit=1000";
public static final String RESERVED_USER_NAME = "test_reserved_user";
public static final String RESERVED_USER_PASSWORD = "reserved_user";
}

@ -20,6 +20,8 @@ package io.entgra.device.mgt.core.apimgt.extension.rest.api.internal;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServices;
import org.wso2.carbon.apimgt.impl.APIManagerConfigurationService;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.tenant.TenantManager;
public class PublisherRESTAPIDataHolder {
@ -28,10 +30,14 @@ public class PublisherRESTAPIDataHolder {
private static PublisherRESTAPIDataHolder thisInstance = new PublisherRESTAPIDataHolder();
private RealmService realmService;
private TenantManager tenantManager;
private PublisherRESTAPIDataHolder() {
}
static PublisherRESTAPIDataHolder getInstance() {
public static PublisherRESTAPIDataHolder getInstance() {
return thisInstance;
}
@ -54,4 +60,27 @@ public class PublisherRESTAPIDataHolder {
return apiManagerConfigurationService;
}
public RealmService getRealmService() {
if (realmService == null) {
throw new IllegalStateException("Realm service is not initialized properly");
}
return realmService;
}
public void setRealmService(RealmService realmService) {
this.realmService = realmService;
this.setTenantManager(realmService);
}
public TenantManager getTenantManager() {
return tenantManager;
}
private void setTenantManager(RealmService realmService) {
if (realmService == null) {
throw new IllegalStateException("Realm service is not initialized properly");
}
this.tenantManager = realmService.getTenantManager();
}
}

@ -110,17 +110,9 @@ public class APIPublisherServiceImpl implements APIPublisherService {
.getOSGiService(RealmService.class, null);
APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl();
APIApplicationKey apiApplicationKey;
AccessTokenInfo accessTokenInfo;
try {
apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
} catch (APIServicesException e) {
String errorMsg = "Error occurred while generating the API application";
log.error(errorMsg, e);
throw new APIManagerPublisherException(e);
}
APIApplicationKey apiApplicationKey = null;
AccessTokenInfo accessTokenInfo = null;
try {
boolean tenantFound = false;
@ -152,9 +144,20 @@ public class APIPublisherServiceImpl implements APIPublisherService {
}
if (tenantFound) {
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(apiConfig.getOwner());
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
} catch (APIServicesException e) {
String errorMsg = "Error occurred while generating the API application";
log.error(errorMsg, e);
throw new APIManagerPublisherException(e);
}
try {
apiConfig.setOwner(APIUtil.getTenantAdminUserName(tenantDomain));
apiConfig.setTenantDomain(tenantDomain);

@ -113,6 +113,11 @@ public class Filter {
*/
private String favouredBy;
/**
* Checking if retired apps needs to be excluded
*/
private boolean isNotRetired;
public int getLimit() {
return limit;
}
@ -208,4 +213,12 @@ public class Filter {
public void setFavouredBy(String favouredBy) {
this.favouredBy = favouredBy;
}
public boolean isNotRetired() {
return isNotRetired;
}
public void setNotRetired(boolean notRetired) {
isNotRetired = notRetired;
}
}

@ -180,6 +180,9 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
if (deviceTypeId != -1) {
sql += "AND AP_APP.DEVICE_TYPE_ID = ? ";
}
if (filter.isNotRetired()) {
sql += "AND AP_APP.STATUS != 'RETIRED' ";
}
sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID ";
if (StringUtils.isNotEmpty(filter.getSortBy())) {
sql += filter.getSortBy() +" ";
@ -308,6 +311,9 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
if (deviceTypeId != -1) {
sql += " AND AP_APP.DEVICE_TYPE_ID = ?";
}
if (filter.isNotRetired()) {
sql += " AND AP_APP.STATUS != 'RETIRED'";
}
try {
conn = this.getDBConnection();

@ -0,0 +1,60 @@
/*
* Copyright (c) 2018 - 2023, 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 io.entgra.device.mgt.core.device.mgt.common.invitation.mgt;
import java.util.Properties;
public class EnrollmentTypeMail {
private String template;
private String username;
private String recipient;
private Properties properties;
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getRecipient() {
return recipient;
}
public void setRecipient(String recipient) {
this.recipient = recipient;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}

@ -0,0 +1,61 @@
/*
* Copyright (c) 2018 - 2023, 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 io.entgra.device.mgt.core.device.mgt.common.invitation.mgt;
public class UserMailAttributes {
private String username;
private String firstName;
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsernamePlaceholder() {
return "username";
}
public String getEmailPlaceholder() {
return "email";
}
public String getFirstNamePlaceholder() {
return "first-name";
}
}

@ -374,6 +374,10 @@
<version>2.3.1.wso2v1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.entgra.device.mgt.core</groupId>
<artifactId>io.entgra.device.mgt.core.apimgt.extension.rest.api</artifactId>
</dependency>
</dependencies>
</project>

@ -121,6 +121,9 @@ public final class DeviceManagementConstants {
public static final String USER_WELCOME_TEMPLATE = "user-welcome";
public static final String DEFAULT_ENROLLMENT_TEMPLATE = "default-enrollment-invitation";
public static final String ENROLLMENT_GUIDE_TEMPLATE = "enrollment-guide";
public static final String DEVICE_ENROLLMENT_MAIL_KEY = "enrollment";
public static final String TEMPLATE_NAME_PART_JOINER = "-";
public static final String ENROLLMENT_TYPE_SPLITTER = "_";
}
public static final class OperationAttributes {

@ -285,32 +285,37 @@ public class ApplicationDAOImpl implements ApplicationDAO {
"MEMORY_USAGE, " +
"IS_ACTIVE, " +
"TENANT_ID " +
"FROM DM_APPLICATION " +
"WHERE NOT EXISTS " +
"(SELECT " +
"ID " +
"FROM DM_APPLICATION A " +
"WHERE A.NAME = DM_APPLICATION.NAME " +
"AND A.ID < DM_APPLICATION.ID) " +
"AND PLATFORM = ? " +
"AND TENANT_ID = ? ";
"FROM DM_APPLICATION " +
"WHERE PLATFORM = ? AND " +
"TENANT_ID = ? AND " +
"NOT EXISTS (SELECT ID " +
"FROM DM_APPLICATION A " +
"WHERE A.NAME = DM_APPLICATION.NAME " +
"AND A.ID < DM_APPLICATION.ID AND " +
"PLATFORM = ? AND TENANT_ID = ?) ";
try {
String filter = request.getFilter();
if (filter != null) {
sql = sql + "AND NAME LIKE ? ";
}
sql = sql + "LIMIT ? OFFSET ?";
if (request != null && request.getRowCount() != -1) {
sql = sql + "LIMIT ? OFFSET ?";
}
Connection conn = this.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setString(paramIdx++, request.getDeviceType());
stmt.setInt(paramIdx++, tenantId);
stmt.setString(paramIdx++, request.getDeviceType());
stmt.setInt(paramIdx++, tenantId);
if (filter != null){
stmt.setString(paramIdx++, filter);
}
stmt.setInt(paramIdx++, request.getRowCount());
stmt.setInt(paramIdx, request.getStartIndex());
if (request != null && request.getRowCount() != -1) {
stmt.setInt(paramIdx++, request.getRowCount());
stmt.setInt(paramIdx, request.getStartIndex());
}
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
application = loadApplication(rs);

@ -17,12 +17,24 @@
*/
package io.entgra.device.mgt.core.device.mgt.core.internal;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServices;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServicesImpl;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServicesImpl;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIApplicationKey;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Scope;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.AccessTokenInfo;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.APIServicesException;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.BadRequestException;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.UnexpectedResponseException;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import io.entgra.device.mgt.core.device.mgt.core.DeviceManagementConstants;
import io.entgra.device.mgt.core.device.mgt.core.DeviceManagementConstants.User;
import org.wso2.carbon.stratos.common.exception.TenantManagementClientException;
import org.wso2.carbon.tenant.mgt.exception.TenantManagementException;
import org.wso2.carbon.user.api.AuthorizationManager;
import org.wso2.carbon.user.api.Permission;
import org.wso2.carbon.user.api.UserRealm;
@ -30,6 +42,10 @@ import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.utils.AbstractAxis2ConfigurationContextObserver;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import java.security.SecureRandom;
import java.util.Stack;
/**
* Load configuration files to tenant's registry.
@ -37,6 +53,7 @@ import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObserver {
private static final Log log = LogFactory.getLog(TenantCreateObserver.class);
/**
* Create configuration context.
*
@ -82,6 +99,29 @@ public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObser
userStoreManager.updateRoleListOfUser(tenantAdminName, null,
new String[] {DeviceManagementConstants.User.DEFAULT_DEVICE_ADMIN,
DeviceManagementConstants.User.DEFAULT_DEVICE_USER});
// String password = this.generateInitialUserPassword();
// createUserIfNotExists("test_reserved_user", password, userStoreManager);
PublisherRESTAPIServices publisherRESTAPIServices = new PublisherRESTAPIServicesImpl();
APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl();
APIApplicationKey apiApplicationKey = null;
AccessTokenInfo accessTokenInfo = null;
try {
apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
} catch (APIServicesException e) {
String errorMsg = "Error occurred while generating the API application";
log.error(errorMsg, e);
throw new TenantManagementException(errorMsg, e);
}
Scope[] scopes = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo);
if (log.isDebugEnabled()) {
log.debug("Device management roles: " + User.DEFAULT_DEVICE_USER + ", " + User.DEFAULT_DEVICE_ADMIN +
" created for the tenant:" + tenantDomain + "."
@ -90,8 +130,50 @@ public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObser
" is assigned to the role:" + User.DEFAULT_DEVICE_ADMIN + "."
);
}
} catch (UserStoreException e) {
} catch (UserStoreException | TenantManagementException e) {
log.error("Error occurred while creating roles for the tenant: " + tenantDomain + ".");
} catch (BadRequestException e) {
throw new RuntimeException(e);
} catch (UnexpectedResponseException e) {
throw new RuntimeException(e);
} catch (APIServicesException e) {
throw new RuntimeException(e);
}
}
private void createUserIfNotExists(String username, String password, UserStoreManager userStoreManager) {
try {
if (!userStoreManager.isExistingUser(MultitenantUtils.getTenantAwareUsername(username))) {
String[] roles = {"admin"};
userStoreManager.addUser(MultitenantUtils.getTenantAwareUsername(username), password, roles, null, "");
userStoreManager.updateCredential(MultitenantUtils.getTenantAwareUsername(username), "reservedpwd", password);
}
} catch (UserStoreException e) {
String msg = "Error when trying to fetch tenant details";
log.error(msg);
}
}
private String generateInitialUserPassword() {
int passwordLength = 6;
//defining the pool of characters to be used for initial password generation
String lowerCaseCharset = "abcdefghijklmnopqrstuvwxyz";
String upperCaseCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String numericCharset = "0123456789";
SecureRandom randomGenerator = new SecureRandom();
String totalCharset = lowerCaseCharset + upperCaseCharset + numericCharset;
int totalCharsetLength = totalCharset.length();
StringBuilder initialUserPassword = new StringBuilder();
for (int i = 0; i < passwordLength; i++) {
initialUserPassword.append(
totalCharset.charAt(randomGenerator.nextInt(totalCharsetLength)));
}
if (log.isDebugEnabled()) {
log.debug("Initial user password is created for new user: " + initialUserPassword);
}
return initialUserPassword.toString();
}
}
}

@ -18,20 +18,12 @@
package io.entgra.device.mgt.core.device.mgt.core.otp.mgt.service;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext;
import io.entgra.device.mgt.core.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.BadRequestException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.DBConnectionException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.DeviceManagementException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.OTPManagementException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.TransactionManagementException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.*;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.DeviceEnrollmentType;
import io.entgra.device.mgt.core.device.mgt.common.otp.mgt.OTPEmailTypes;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.EnrollmentTypeMail;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.UserMailAttributes;
import io.entgra.device.mgt.core.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import io.entgra.device.mgt.core.device.mgt.common.spi.OTPManagementService;
import io.entgra.device.mgt.core.device.mgt.core.DeviceManagementConstants;
@ -40,20 +32,16 @@ import io.entgra.device.mgt.core.device.mgt.core.otp.mgt.dao.OTPManagementDAO;
import io.entgra.device.mgt.core.device.mgt.core.otp.mgt.dao.OTPManagementDAOFactory;
import io.entgra.device.mgt.core.device.mgt.core.otp.mgt.exception.OTPManagementDAOException;
import io.entgra.device.mgt.core.device.mgt.core.otp.mgt.util.ConnectionManagerUtil;
import io.entgra.device.mgt.core.device.mgt.core.service.DeviceManagementProviderService;
import io.entgra.device.mgt.core.device.mgt.core.service.EmailMetaInfo;
import io.entgra.device.mgt.core.device.mgt.core.util.DeviceManagerUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.user.api.Tenant;
import org.wso2.carbon.user.api.UserStoreException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.*;
public class OTPManagementServiceImpl implements OTPManagementService {
@ -218,53 +206,12 @@ public class OTPManagementServiceImpl implements OTPManagementService {
}
}
@Override
public void sendDeviceEnrollmentInvitationMail(DeviceEnrollmentInvitation deviceEnrollmentInvitation)
throws OTPManagementException {
DeviceManagementProviderService dms = DeviceManagementDataHolder.getInstance().getDeviceManagementProvider();
StringBuilder enrollmentSteps = new StringBuilder();
DeviceEnrollmentInvitationDetails deviceEnrollmentInvitationDetails;
for (DeviceEnrollmentType deviceEnrollmentType : deviceEnrollmentInvitation.getDeviceEnrollmentTypes()) {
deviceEnrollmentInvitationDetails = dms.getDeviceEnrollmentInvitationDetails(
deviceEnrollmentType.getDeviceType());
if (deviceEnrollmentInvitationDetails != null &&
deviceEnrollmentInvitationDetails.getEnrollmentDetails() != null) {
for (String enrollmentType : deviceEnrollmentType.getEnrollmentType()) {
deviceEnrollmentInvitationDetails.getEnrollmentDetails().stream()
.filter(details -> enrollmentType.equals(details.getEnrollmentType())).findFirst()
.ifPresent(details -> enrollmentSteps.append(details.getEnrollmentSteps()));
}
}
}
Properties props = new Properties();
props.setProperty("enrollment-steps", enrollmentSteps.toString());
try {
ConnectionManagerUtil.beginDBTransaction();
for (String username : deviceEnrollmentInvitation.getUsernames()) {
String emailAddress = DeviceManagerUtil.getUserClaimValue(
username, DeviceManagementConstants.User.CLAIM_EMAIL_ADDRESS);
props.setProperty("first-name", DeviceManagerUtil.
getUserClaimValue(username, DeviceManagementConstants.User.CLAIM_FIRST_NAME));
props.setProperty("username", username);
sendMail(props, emailAddress, DeviceManagementConstants.EmailAttributes.USER_ENROLLMENT_TEMPLATE);
}
ConnectionManagerUtil.commitDBTransaction();
} catch (UserStoreException e) {
String msg = "Error occurred while getting claim values to invite user";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection to add OPT data.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (TransactionManagementException e) {
String msg = "SQL Error occurred when adding OPT data to send device enrollment Invitation.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
List<EnrollmentTypeMail> enrollmentTypeMails =
getEnrollmentTypeMails(deviceEnrollmentInvitation.getDeviceEnrollmentTypes());
sendEnrollmentTypeMails(deviceEnrollmentInvitation.getUsernames(), enrollmentTypeMails);
}
/**
@ -296,7 +243,7 @@ public class OTPManagementServiceImpl implements OTPManagementService {
throw new OTPManagementException(msg, e);
} catch (OTPManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while saving the OTP data for given email" ;
String msg = "Error occurred while saving the OTP data for given email";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
@ -312,7 +259,7 @@ public class OTPManagementServiceImpl implements OTPManagementService {
* @return {@link OneTimePinDTO}
* @throws OTPManagementException if error occurred while getting OTP data for given OTP in DB
*/
private OneTimePinDTO getOTPDataByToken (String oneTimeToken) throws OTPManagementException {
private OneTimePinDTO getOTPDataByToken(String oneTimeToken) throws OTPManagementException {
try {
ConnectionManagerUtil.openDBConnection();
return otpManagementDAO.getOTPDataByToken(oneTimeToken);
@ -333,7 +280,7 @@ public class OTPManagementServiceImpl implements OTPManagementService {
* If OTP expired, resend the user verifying mail with renewed OTP
* @param props Mail body properties
* @param mailAddress Mail Address of the User
* @param template Mail template to be used
* @param template Mail template to be used
* @throws OTPManagementException if error occurred while resend the user verifying mail
*/
private void sendMail(Properties props, String mailAddress, String template) throws OTPManagementException {
@ -355,7 +302,7 @@ public class OTPManagementServiceImpl implements OTPManagementService {
/**
* Renew the OTP
* @param oneTimePinDTO {@link OneTimePinDTO}
* @param renewedOTP Renewed OTP
* @param renewedOTP Renewed OTP
* @throws OTPManagementException if error occurred while renew the OTP
*/
private void renewOTP(OneTimePinDTO oneTimePinDTO, String renewedOTP) throws OTPManagementException {
@ -380,4 +327,113 @@ public class OTPManagementServiceImpl implements OTPManagementService {
ConnectionManagerUtil.closeDBConnection();
}
}
/**
* Send enrollment type mails to users
* @param usernames List of usernames to send enrollment type mails
* @param enrollmentTypeMails List of enrollment types
* @throws OTPManagementException Throws when error occurred while sending emails
*/
private void sendEnrollmentTypeMails(List<String> usernames, List<EnrollmentTypeMail> enrollmentTypeMails)
throws OTPManagementException {
try {
ConnectionManagerUtil.beginDBTransaction();
for (String username : usernames) {
populateUserAttributes(getUserMailAttributes(username), enrollmentTypeMails);
for (EnrollmentTypeMail enrollmentTypeMail : enrollmentTypeMails) {
sendMail(enrollmentTypeMail);
}
}
ConnectionManagerUtil.commitDBTransaction();
} catch (UserStoreException e) {
String msg = "Error occurred while populating user attributes";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection to add OTP data.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (TransactionManagementException e) {
String msg = "SQL Error occurred when adding OPT data to send device enrollment Invitation.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
/**
* Send enrollment type mail
* @param enrollmentTypeMail Data related to the enrollment mail
* @throws OTPManagementException Throws when error occurred while sending email
*/
private void sendMail(EnrollmentTypeMail enrollmentTypeMail) throws OTPManagementException {
sendMail(enrollmentTypeMail.getProperties(), enrollmentTypeMail.getRecipient(), enrollmentTypeMail.getTemplate());
}
/**
* Get user claims based on the username
* @param username Username
* @return {@link UserMailAttributes}
* @throws UserStoreException Throws when error occurred while retrieving user claims
*/
private UserMailAttributes getUserMailAttributes(String username) throws UserStoreException {
UserMailAttributes userMailAttributes = new UserMailAttributes();
userMailAttributes.setEmail(DeviceManagerUtil.getUserClaimValue(
username, DeviceManagementConstants.User.CLAIM_EMAIL_ADDRESS));
userMailAttributes.setFirstName(DeviceManagerUtil.
getUserClaimValue(username, DeviceManagementConstants.User.CLAIM_FIRST_NAME));
userMailAttributes.setUsername(username);
return userMailAttributes;
}
/**
* Populate enrollment type mails with provided user attributes
* @param userMailAttributes User attributes
* @param enrollmentTypeMails Enrollment type mails
*/
private void populateUserAttributes(UserMailAttributes userMailAttributes, List<EnrollmentTypeMail> enrollmentTypeMails) {
for (EnrollmentTypeMail enrollmentTypeMail : enrollmentTypeMails) {
Properties properties = new Properties();
properties.setProperty(userMailAttributes.getEmailPlaceholder(), userMailAttributes.getEmail());
properties.setProperty(userMailAttributes.getFirstNamePlaceholder(), userMailAttributes.getFirstName());
properties.setProperty(userMailAttributes.getUsernamePlaceholder(), userMailAttributes.getUsername());
enrollmentTypeMail.setProperties(properties);
enrollmentTypeMail.setUsername(userMailAttributes.getUsername());
enrollmentTypeMail.setRecipient(userMailAttributes.getEmail());
}
}
/**
* Generate enrollment type mail
* @param deviceType Device type of the enrollment type
* @param enrollmentType Enrollment type
* @return {@link EnrollmentTypeMail}
*/
private EnrollmentTypeMail getEnrollmentTypeMail(String deviceType, String enrollmentType) {
EnrollmentTypeMail enrollmentTypeMail = new EnrollmentTypeMail();
enrollmentTypeMail.setUsername(enrollmentTypeMail.getUsername());
enrollmentTypeMail.setTemplate(String.join(DeviceManagementConstants.EmailAttributes.TEMPLATE_NAME_PART_JOINER,
deviceType.toLowerCase(), enrollmentType.toLowerCase().
replace(DeviceManagementConstants.EmailAttributes.ENROLLMENT_TYPE_SPLITTER,
DeviceManagementConstants.EmailAttributes.TEMPLATE_NAME_PART_JOINER),
DeviceManagementConstants.EmailAttributes.DEVICE_ENROLLMENT_MAIL_KEY));
return enrollmentTypeMail;
}
/**
* Generate enrollment type mails from device enrollment types
* @param deviceEnrollmentTypes List of device enrollment types
* @return List of enrollment type mails
*/
private List<EnrollmentTypeMail> getEnrollmentTypeMails(List<DeviceEnrollmentType> deviceEnrollmentTypes) {
List<EnrollmentTypeMail> enrollmentTypeMails = new ArrayList<>();
for (DeviceEnrollmentType deviceEnrollmentType : deviceEnrollmentTypes) {
String deviceType = deviceEnrollmentType.getDeviceType();
for (String enrollmentType : deviceEnrollmentType.getEnrollmentType()) {
enrollmentTypeMails.add(getEnrollmentTypeMail(deviceType, enrollmentType));
}
}
return enrollmentTypeMails;
}
}

@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS AP_APP(
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS AP_APP_RELEASE(
ID INTEGER NOT NULL AUTO_INCREMENT,
DESCRIPTION VARCHAR(200) NOT NULL,
DESCRIPTION TEXT NOT NULL,
VERSION VARCHAR(70) NOT NULL,
TENANT_ID INTEGER NOT NULL,
UUID VARCHAR(200) NOT NULL,
@ -34,7 +34,7 @@ CREATE TABLE IF NOT EXISTS AP_APP_RELEASE(
SC_3_LOCATION VARCHAR(100) NULL DEFAULT NULL,
APP_HASH_VALUE VARCHAR(1000) NOT NULL,
SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE,
APP_META_INFO VARCHAR(150) NULL DEFAULT NULL,
APP_META_INFO TEXT NULL DEFAULT NULL,
SUPPORTED_OS_VERSIONS VARCHAR(45) NOT NULL,
RATING DOUBLE NULL DEFAULT NULL,
CURRENT_STATE VARCHAR(45) NOT NULL,
@ -328,4 +328,4 @@ CREATE TABLE IF NOT EXISTS AP_VPP_ASSOCIATION (
PRIMARY KEY (ID),
CONSTRAINT AP_VPP_ASSETS_fk FOREIGN KEY (ASSET_ID) REFERENCES AP_ASSETS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT AP_VPP_VPP_USER_fk FOREIGN KEY (USER_ID) REFERENCES AP_VPP_USER (ID) ON DELETE CASCADE ON UPDATE CASCADE
);
);

@ -15,56 +15,440 @@
specific language governing permissions and limitations
under the License.
*#
<EmailConfig>
<Subject>You have successfully been registered in Entgra IoT</Subject>
<Subject>You have been invited to enroll your device in Entgra UEM</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="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 $first-name,
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
You have been registered in Entgra IoT and invited to enrol your device.
Click <a href="$base-url-https/endpoint-mgt/devices/enroll">here</a> to begin device enrolment.</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Use following credentials to log in to Entgra IoT Device Management application.
</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
<b>Username:</b> $username
<br/>
<b>Password:</b> $password
</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Should you need assistance, please contact your administrator.
</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
Regards,
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Entgra IoT Administrator
</p>
</div>
</div>
</div>
</body>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG>
</o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<!--[if !mso]><!-->
<link href="https://fonts.googleapis.com/css?family=Cabin:400,700" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css" />
<!--<![endif]-->
<title>Entgra UEM Server</title>
<style type="text/css">
@media only screen and (min-width: 620px) {
.u-row {
width: 600px !important;
}
.u-row .u-col {
vertical-align: top;
}
.u-row .u-col-100 {
width: 600px !important;
}
}
@media (max-width: 620px) {
.u-row-container {
max-width: 100% !important;
padding-left: 0px !important;
padding-right: 0px !important;
}
.u-row .u-col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important;
}
.u-row {
width: 100% !important;
}
.u-col {
width: 100% !important;
}
.u-col > div {
margin: 0 auto;
}
}
body {
margin: 0;
padding: 0;
}
table,
tr,
td {
vertical-align: top;
border-collapse: collapse;
}
p {
margin: 0;
}
.ie-container table,
.mso-container table {
table-layout: fixed;
}
* {
line-height: inherit;
}
a[x-apple-data-detectors='true'] {
color: inherit !important;
text-decoration: none !important;
}
@media (min-width: 481px) and (max-width: 768px) {
}
table, td { color: #000000; } #u_body a { color: #0000ee; text-decoration: underline; }
</style>
</head>
<body class="clean-body u_body" style="margin: 0;padding: 0;-webkit-text-size-adjust: 100%;background-color: #f9f9f9;color: #000000">
<!--[if IE]>
<div class="ie-container">
<![endif]-->
<!--[if mso]>
<div class="mso-container">
<![endif]-->
<table id="u_body" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;min-width: 320px;Margin: 0 auto;background-color: #f9f9f9;width:100%" cellpadding="0" cellspacing="0">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<!--[if (mso)|(IE)]>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="center" style="background-color: #f9f9f9;">
<![endif]-->
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding: 0px;background-color: transparent;" align="center">
<table cellpadding="0" cellspacing="0" border="0" style="width:600px;">
<tr style="background-color: transparent;">
<![endif]-->
<!--[if (mso)|(IE)]>
<td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;" valign="top">
<![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;">
<!--[if (!mso)&amp;(!IE)]><!-->
<div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;">
<!--<![endif]-->
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:0px;font-family:'Cabin',sans-serif;" align="left">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding-right: 0px;padding-left: 0px;" align="center">
<img align="center" border="0" src="https://storage.googleapis.com/cdn-entgra/try-it/assets/imgs/uem_reg_confirm_hero.jpg" alt="" title="" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: inline-block !important;border: none;height: auto;float: none;width: 100%;max-width: 600px;" width="600"/>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&amp;(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
</tr>
</table>
</td>
</tr>
</table>
<![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding: 0px;background-color: transparent;" align="center">
<table cellpadding="0" cellspacing="0" border="0" style="width:600px;">
<tr style="background-color: transparent;">
<![endif]-->
<!--[if (mso)|(IE)]>
<td align="center" width="600" style="background-color: #ffffff;width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;" valign="top">
<![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
<div style="background-color: #ffffff;height: 100%;width: 100% !important;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;">
<!--[if (!mso)&amp;(!IE)]><!-->
<div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;">
<!--<![endif]-->
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:30px;font-family:'Cabin',sans-serif;" align="left">
<div style="font-family: 'Montserrat',sans-serif; font-size: 14px; line-height: 140%; text-align: left; word-wrap: break-word;">
<p style="line-height: 140%;">Dear $first-name,</p>
<br />
<p style="line-height: 140%;">We have completed your Entgra UEM registration. You are now invited to proceed with the enrollment of your device.</p>
<br />
<br />
<p style="line-height: 140%;">Please click on the link below to initiate this process.</p>
</div>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding-left:30px;font-family:'Cabin',sans-serif;" align="left">
<!--[if mso]>
<style>.v-button {background: transparent !important;}</style>
<![endif]-->
<div align="left">
<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="$base-url-https/endpoint-mgt/devices/enroll" style="height:37px; v-text-anchor:middle; width:156px;" arcsize="11%" stroke="f" fillcolor="#3f84bb">
<w:anchorlock/>
<center style="color:#FFFFFF;font-family: 'Montserrat',sans-serif; ">
<![endif]-->
<a href="$base-url-https/endpoint-mgt/devices/enroll" target="_blank" class="v-button" style="box-sizing: border-box;display: inline-block;text-decoration: none;-webkit-text-size-adjust: none;text-align: left;color: #FFFFFF; background-color: #3f84bb; border-radius: 4px;-webkit-border-radius: 4px; -moz-border-radius: 4px; width:auto; max-width:100%; overflow-wrap: break-word; word-break: break-word; word-wrap:break-word; mso-border-alt: none;font-family: 'Montserrat',sans-serif; font-size: 14px;">
<span style="display:block;padding:10px 20px;line-height:120%;">Start enrollment</span>
</a>
<!--[if mso]>
</center>
</v:roundrect>
<![endif]-->
</div>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:30px;font-family:'Cabin',sans-serif;" align="left">
<div style="font-family: 'Montserrat',sans-serif; font-size: 14px; line-height: 140%; text-align: left; word-wrap: break-word;">
<p style="line-height: 140%;">Here are your login credentials:</p>
<br />
<p style="line-height: 140%;">Username: $username</p>
<p style="line-height: 140%;">Password: $password</p>
<br />
<p style="line-height: 140%;">If you require any assistance or have questions during the enrollment process, reach out to your designated administrator via the <a rel="noopener" href="https://support.entgra.net/" target="_blank">Entgra Support Portal. </a></p>
<br />
<p style="line-height: 140%;">Thank you.</p>
<br />
<p style="line-height: 140%;">Best wishes,</p>
<p style="line-height: 140%;">Entgra team</p>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&amp;(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
</tr>
</table>
</td>
</tr>
</table>
<![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding: 0px;background-color: transparent;" align="center">
<table cellpadding="0" cellspacing="0" border="0" style="width:600px;">
<tr style="background-color: transparent;">
<![endif]-->
<!--[if (mso)|(IE)]>
<td align="center" width="600" style="background-color: #ffffff;width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;" valign="top">
<![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
<div style="background-color: #ffffff;height: 100%;width: 100% !important;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;">
<!--[if (!mso)&amp;(!IE)]><!-->
<div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;border-radius: 0px;-webkit-border-radius: 0px; -moz-border-radius: 0px;">
<!--<![endif]-->
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Cabin',sans-serif;" align="left">
<table height="0px" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 1px solid #BBBBBB;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;font-size: 0px;line-height: 0px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:30px 10px 10px;font-family:'Cabin',sans-serif;" align="left">
<div style="font-family: 'Montserrat',sans-serif; font-size: 10px; color: #3f84bb; line-height: 140%; text-align: center; word-wrap: break-word;">
<p style="line-height: 140%;"><a rel="noopener" href="https://entgra.io/privacy-policy/" target="_blank">Privacy policy</a></p>
</div>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Cabin',sans-serif;" align="left">
<div style="font-family: 'Montserrat',sans-serif; font-size: 14px; line-height: 140%; text-align: center; word-wrap: break-word;">
<p style="line-height: 140%;">Follow Entgra on social media</p>
</div>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Cabin',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px 10px 30px;font-family:'Cabin',sans-serif;" align="left">
<div align="center">
<div style="display: table; max-width:147px;">
<!--[if (mso)|(IE)]>
<table width="147" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="border-collapse:collapse;" align="center">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse; mso-table-lspace: 0pt;mso-table-rspace: 0pt; width:147px;">
<tr>
<![endif]-->
<!--[if (mso)|(IE)]>
<td width="32" style="width:32px; padding-right: 5px;" valign="top">
<![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 5px">
<tbody>
<tr style="vertical-align: top">
<td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://web.facebook.com/entgra/?_rdc=1&amp;_rdr" title="Facebook" target="_blank">
<img src="https://storage.googleapis.com/cdn-entgra/try-it/assets/icons/facebook_icon.png" alt="Facebook" title="Facebook" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important" />
</a>
</td>
</tr>
</tbody>
</table>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
<td width="32" style="width:32px; padding-right: 5px;" valign="top">
<![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 5px">
<tbody>
<tr style="vertical-align: top">
<td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://www.linkedin.com/authwall?trk=bf&amp;trkInfo=AQF27xf5mHyOcgAAAYr7u78geOJx3p5gqeVPOR61q4iuk7LkOiZmPZf09b2dHKHzYqTIkB1D3XL9YSlCJKEj9O_xBBP9lw7zVImR04IBnmHuQ0n2cAwziOd1KwbNmOuWBtpiwCM=&amp;original_referer=&amp;sessionRedirect=https%3A%2F%2Fwww.linkedin.com%2Fcompany%2Fentgra%2Fmycompany%2F" title="LinkedIn" target="_blank">
<img src="https://storage.googleapis.com/cdn-entgra/try-it/assets/icons/linkedin_icon.png" alt="LinkedIn" title="LinkedIn" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important" />
</a>
</td>
</tr>
</tbody>
</table>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
<td width="32" style="width:32px; padding-right: 5px;" valign="top">
<![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 5px">
<tbody>
<tr style="vertical-align: top">
<td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://twitter.com/entgra_io" title="X" target="_blank">
<img src="https://storage.googleapis.com/cdn-entgra/try-it/assets/icons/twitter_icon.png" alt="X" title="X" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important" />
</a>
</td>
</tr>
</tbody>
</table>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
<td width="32" style="width:32px; padding-right: 0px;" valign="top">
<![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 0px">
<tbody>
<tr style="vertical-align: top">
<td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://www.youtube.com/channel/UCJVV8k92SzvvyWxvv01vjPg" title="YouTube" target="_blank">
<img src="https://storage.googleapis.com/cdn-entgra/try-it/assets/icons/youtube_icon.png" alt="YouTube" title="YouTube" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important" />
</a>
</td>
</tr>
</tbody>
</table>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
</tr>
</table>
</td>
</tr>
</table>
<![endif]-->
</div>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&amp;(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]>
</td>
<![endif]-->
<!--[if (mso)|(IE)]>
</tr>
</table>
</td>
</tr>
</table>
<![endif]-->
</div>
</div>
</div>
<!--[if (mso)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if mso]>
</div>
<![endif]-->
<!--[if IE]>
</div>
<![endif]-->
</body>
</html>
]]>
</Body>

Loading…
Cancel
Save