Merge pull request 'Add JIT base provision and enrollment handlers' (#230) from rajitha/device-mgt-core:jit-feature into master

Reviewed-on: community/device-mgt-core#230
remotes/1712966534876109980/master
Pahansith Gunathilake 11 months ago
commit 9ae64c718c

@ -0,0 +1,28 @@
package io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "InvitationMailProfile", description = "Holds data related to JIT Enrollment invitation mails")
public class InvitationMailProfile {
@ApiModelProperty(name = "username", value = "Username (same as username in external IDP)", required = true)
private String username;
@ApiModelProperty(name = "mail", value = "Mail will be sent to this mail address", required = true)
private String mail;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
}

@ -0,0 +1,50 @@
package io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
@ApiModel(value = "JITEnrollmentInvitation", description = "Holds data related to JIT enrollment invitations")
public class JITEnrollmentInvitation {
@ApiModelProperty(name = "mailProfiles", value = "Mail profiles to send mail invitations", required = true)
private List<InvitationMailProfile> mailProfiles;
@ApiModelProperty(name = "ownershipType", value = "Ownership type of the enrollment", required = true)
private String ownershipType;
@ApiModelProperty(name = "deviceType", value = "Device type", required = true)
private String deviceType;
@ApiModelProperty(name = "sp", value = "Service provider name", required = true)
private String sp;
public List<InvitationMailProfile> getMailProfiles() {
return mailProfiles;
}
public void setMailProfiles(List<InvitationMailProfile> mailProfiles) {
this.mailProfiles = mailProfiles;
}
public String getOwnershipType() {
return ownershipType;
}
public void setOwnershipType(String ownershipType) {
this.ownershipType = ownershipType;
}
public String getDeviceType() {
return deviceType;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public String getSp() {
return sp;
}
public void setSp(String sp) {
this.sp = sp;
}
}

@ -18,33 +18,34 @@
package io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.api;
import com.google.gson.JsonArray;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Info;
import io.swagger.annotations.ExtensionProperty;
import io.swagger.annotations.Extension;
import io.swagger.annotations.Tag;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.ResponseHeader;
import org.apache.axis2.transport.http.HTTPConstants;
import io.entgra.device.mgt.core.apimgt.annotations.Scopes;
import io.entgra.device.mgt.core.apimgt.annotations.Scope;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import io.entgra.device.mgt.core.apimgt.annotations.Scopes;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.ActivityList;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.BasicUserInfo;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.BasicUserInfoList;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.Credential;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.EnrollmentInvitation;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.ErrorResponse;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.JITEnrollmentInvitation;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.OldPasswordResetWrapper;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.PermissionList;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.RoleList;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.UserInfo;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.UserStoreList;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.util.Constants;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
import io.swagger.annotations.Info;
import io.swagger.annotations.ResponseHeader;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
import org.apache.axis2.transport.http.HTTPConstants;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
@ -54,7 +55,6 @@ import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
@ -940,6 +940,49 @@ public interface UserManagementService {
required = true)
@Valid EnrollmentInvitation enrollmentInvitation);
@POST
@Path("/jit-enrollment-invite")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = HTTPConstants.HEADER_POST,
value = "Sending Enrollment Invitations to email address",
notes = "Send the a mail inviting recipients to enroll devices.",
tags = "User Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:users:send-invitation")
})
}
)
@ApiResponses(value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully sent the invitation mail."),
@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.\n",
response = ErrorResponse.class),
@ApiResponse(
code = 415,
message = "Unsupported media type. \n The format of the requested entity was not supported.\n",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred while updating the user credentials.",
response = ErrorResponse.class)
})
Response inviteExternalUsers(
@ApiParam(
name = "jitEnrollmentInvitation",
value = "List of email address of recipients",
required = true)
@Valid JITEnrollmentInvitation jitEnrollmentInvitation);
@POST
@Path("/validate")
Response validateUser(Credential credential);

@ -19,7 +19,6 @@ package io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.entgra.device.mgt.core.device.mgt.common.Device;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -28,7 +27,6 @@ import org.eclipse.wst.common.uriresolver.internal.util.URIEncoder;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.DeviceManagementException;
import io.entgra.device.mgt.core.device.mgt.common.EnrolmentInfo;
import io.entgra.device.mgt.core.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.OTPManagementException;
import io.entgra.device.mgt.core.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
@ -45,6 +43,8 @@ import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.BasicUserInfoWrapper
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.Credential;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.EnrollmentInvitation;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.ErrorResponse;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.InvitationMailProfile;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.JITEnrollmentInvitation;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.OldPasswordResetWrapper;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.PermissionList;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.RoleList;
@ -83,7 +83,6 @@ import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
@ -92,6 +91,7 @@ import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.NoSuchFileException;
import java.security.SecureRandom;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@ -757,6 +757,54 @@ public class UserManagementServiceImpl implements UserManagementService {
return Response.status(Response.Status.OK).entity("Invitation mails have been sent.").build();
}
@POST
@Path("jit-enrollment-invite")
@Override
public Response inviteExternalUsers(JITEnrollmentInvitation jitEnrollmentInvitation) {
if (jitEnrollmentInvitation.getMailProfiles() == null || jitEnrollmentInvitation.getMailProfiles().isEmpty()) {
String msg = "Error occurred while validating mail profiles. Mail profiles cannot be empty";
log.error(msg);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).setCode(HttpStatus.SC_BAD_REQUEST).
build());
}
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
String inviteBy = DeviceMgtAPIUtils.getAuthenticatedUser();
try {
DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService();
for (InvitationMailProfile mailProfile : jitEnrollmentInvitation.getMailProfiles()) {
Properties props = new Properties();
props.setProperty("username", mailProfile.getUsername());
props.setProperty("tenant-domain", tenantDomain);
props.setProperty("sp", jitEnrollmentInvitation.getSp());
props.setProperty("ownership-type", jitEnrollmentInvitation.getOwnershipType());
props.setProperty("device-type", jitEnrollmentInvitation.getDeviceType());
props.setProperty("invite-by", inviteBy);
Set<String> recipients = new HashSet<>();
recipients.add(mailProfile.getMail());
EmailMetaInfo metaInfo = new EmailMetaInfo(recipients, props);
dms.sendEnrolmentInvitation(getTemplateName(jitEnrollmentInvitation.getDeviceType(),
"jit-enrollment-invitation", "-"), metaInfo);
}
} catch (DeviceManagementException ex) {
String msg = "Error occurred while inviting user to enroll their device";
log.error(msg, ex);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (ConfigurationManagementException ex) {
String msg = "Error occurred while sending the email invitations. Mail server not configured.";
log.error(msg, ex);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (NoSuchFileException ex) {
String msg = "Error occurred while retrieving email template";
log.error(msg, ex);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
return Response.status(Response.Status.OK).entity("Invitation mails have been sent.").build();
}
@POST
@Path("/validate")
@Override
@ -1179,6 +1227,30 @@ public class UserManagementServiceImpl implements UserManagementService {
return DeviceManagementConstants.EmailAttributes.DEFAULT_ENROLLMENT_TEMPLATE;
}
private String getTemplateName(String deviceType, String prefix, String separator) throws NoSuchFileException {
String templateName = deviceType + separator + prefix + ".vm";
List<String> templatePathSegments =
Arrays.asList(CarbonUtils.getCarbonHome(), "repository", "resources", "email-templates", templateName);
File template = new File(String.join(File.separator, templatePathSegments));
if (template.exists()) {
return templateName;
}
String defaultTemplateName = "default" + separator + prefix + ".vm";
List<String> defaultTemplatePathSegments =
Arrays.asList(CarbonUtils.getCarbonHome(), "repository", "resources", "email-templates", defaultTemplateName);
File defaultTemplate = new File(String.join(File.separator, defaultTemplatePathSegments));
if (defaultTemplate.exists()) {
if (log.isDebugEnabled()) {
log.debug("The template that is expected to use is not available. Therefore, using default template.");
}
return defaultTemplateName;
}
throw new NoSuchFileException("Didn't found template file for " + templateName);
}
/**
* Searches users which matches a given filter based on a claim
*

@ -0,0 +1,277 @@
/*
* 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.ui.request.interceptor;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.AuthData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITEnrollmentData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITEnrollmentException;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wso2.carbon.utils.CarbonUtils;
import org.xml.sax.SAXException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import java.util.Objects;
@WebServlet(
name = "JIT Enrollment callback handler",
description = "Call token endpoint and retrieve token",
urlPatterns = {
"/jit-enrollment-callback"
}
)
public class JITEnrollmentCallbackHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITEnrollmentCallbackHandler.class);
private String gatewayUrl;
private String keyManagerUrl;
private JITData JITInfo;
private String encodedClientCredentials;
private String applicationName;
private String clientId;
private String clientSecret;
private String scope;
private String JITConfigurationPath;
private JITEnrollmentData JITEnrollmentInfo;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
gatewayUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getGatewayPort(request.getScheme());
keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme());
JITConfigurationPath = CarbonUtils.getCarbonConfigDirPath() + File.separator + "jit-config.xml";
HttpSession session = request.getSession(false);
try {
if (session == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED);
return;
}
JITInfo = (JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY);
if (JITInfo == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED);
return;
}
JITEnrollmentInfo = (JITEnrollmentData)
session.getAttribute(HandlerConstants.SESSION_JIT_ENROLLMENT_DATA_KEY);
if (JITEnrollmentInfo == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED);
return;
}
applicationName = request.getContextPath().substring(1,
request.getContextPath().indexOf("-ui-request-handler"));
initializeJITEnrollmentConfigurations();
populateApplicationData(registerApplication());
persistAuthData(session, getToken());
response.sendRedirect(JITEnrollmentInfo.getRedirectUrl() + "?ownershipType=" +
JITEnrollmentInfo.getOwnershipType() + "&os=" + JITEnrollmentInfo.getOs() + "&username=" +
JITEnrollmentInfo.getUsername() + "&tenantDomain=" + JITEnrollmentInfo.getTenantDomain());
} catch (JITEnrollmentException | IOException ex) {
log.error("Error occurred while processing JIT provisioning callback request", ex);
}
}
private void initializeJITEnrollmentConfigurations() throws JITEnrollmentException {
try {
File JITConfigurationFile = new File(JITConfigurationPath);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document JITConfigurationDoc = documentBuilder.parse(JITConfigurationFile);
JITConfigurationDoc.getDocumentElement().normalize();
Element enrollmentScopes;
if (Objects.equals(JITEnrollmentInfo.getOs(), HandlerConstants.OS_ANDROID)) {
enrollmentScopes = (Element) JITConfigurationDoc.
getElementsByTagName(HandlerConstants.TAG_ANDROID_ENROLLMENT_SCOPES).item(0);
} else if (Objects.equals(JITEnrollmentInfo.getOs(), HandlerConstants.OS_IOS)) {
enrollmentScopes = (Element) JITConfigurationDoc.
getElementsByTagName(HandlerConstants.TAG_IOS_ENROLLMENT_SCOPES).item(0);
} else if (Objects.equals(JITEnrollmentInfo.getOs(), HandlerConstants.OS_WINDOWS)) {
enrollmentScopes = (Element) JITConfigurationDoc.
getElementsByTagName(HandlerConstants.TAG_WINDOWS_ENROLLMENT_SCOPES).item(0);
} else {
String msg = "OS type not supported";
if (log.isDebugEnabled()) {
log.error(msg);
}
throw new JITEnrollmentException(msg);
}
NodeList scopeList = enrollmentScopes.getElementsByTagName("Scope");
StringBuilder scopeStr = new StringBuilder();
for (int idx = 0; idx < scopeList.getLength(); idx++) {
Node scopeNode = scopeList.item(idx);
if (scopeNode.getNodeType() == Node.ELEMENT_NODE) {
Element scopeElement = (Element) scopeNode;
scopeStr.append(" ").append(scopeElement.getTextContent());
}
}
scope = scopeStr.toString();
} catch (ParserConfigurationException ex) {
String msg = "Error occurred when document builder creating the file configuration";
throw new JITEnrollmentException(msg, ex);
} catch (IOException ex) {
String msg = "IO error occurred while parsing the JIT config file";
throw new JITEnrollmentException(msg, ex);
} catch (SAXException ex) {
String msg = "Parse error occurred while parsing the JIT config document";
throw new JITEnrollmentException(msg, ex);
}
}
/***
* Parse string data and build json object
* @param data - Json string
* @return {@link JsonObject} Json object corresponding to provided json string
* @throws JITEnrollmentException throws when error occurred while parsing
*/
private JsonObject parseResponseData(String data) throws JITEnrollmentException {
JsonParser parser = new JsonParser();
JsonElement responseData = parser.parse(data);
if (responseData.isJsonObject()) {
return responseData.getAsJsonObject();
}
throw new JITEnrollmentException("Unexpected response body return");
}
/***
* Build application registration request
* @return {@link HttpPost} Application registration request
*/
private HttpPost buildApplicationRegistrationRequest() {
HttpPost applicationRegistrationRequest = new HttpPost(gatewayUrl + HandlerConstants.APP_REG_ENDPOINT);
applicationRegistrationRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC
+ JITInfo.getEncodedClientCredentials());
applicationRegistrationRequest.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
JsonArray tags = new JsonArray();
tags.add("device_management");
JsonObject payload = new JsonObject();
payload.addProperty("applicationName", applicationName);
payload.add("tags", tags);
payload.addProperty("allowedToAllDomains", false);
payload.addProperty("mappingAnExistingOAuthApp", false);
applicationRegistrationRequest.setEntity(new StringEntity(payload.toString(), ContentType.APPLICATION_JSON));
return applicationRegistrationRequest;
}
/***
* Populate dynamic client's data
* @param application - application data receiving from dcr request
*/
private void populateApplicationData(JsonObject application) {
clientId = application.get("client_id").getAsString();
clientSecret = application.get("client_secret").getAsString();
String headerValue = clientId+ ':' + clientSecret;
encodedClientCredentials = Base64.getEncoder().encodeToString(headerValue.getBytes());
}
/***
* Register client application
* @return {@link JsonObject} Json object contain registered application data
* @throws JITEnrollmentException throws when error occurred while application registration
*/
private JsonObject registerApplication() throws JITEnrollmentException {
try {
ProxyResponse proxyResponse = HandlerUtil.execute(buildApplicationRegistrationRequest());
if (proxyResponse.getCode() == HttpStatus.SC_CREATED ||
proxyResponse.getCode() == HttpStatus.SC_OK) {
return parseResponseData(proxyResponse.getData());
}
throw new JITEnrollmentException("Unexpected response status return for application registration request");
} catch (IOException ex) {
throw new JITEnrollmentException("Error occurred while executing application registration request", ex);
}
}
/***
* Acquire token
* @return {@link JsonObject} Json object containing token data
* @throws JITEnrollmentException throws when error occurred while acquiring token
*/
private JsonObject getToken() throws JITEnrollmentException {
try {
ProxyResponse proxyResponse = HandlerUtil.execute(buildTokenAcquireRequest());
if (proxyResponse.getCode() == org.apache.http.HttpStatus.SC_CREATED ||
proxyResponse.getCode() == org.apache.http.HttpStatus.SC_OK) {
return parseResponseData(proxyResponse.getData());
}
throw new JITEnrollmentException("Unexpected response status return for token acquiring request");
} catch (IOException ex) {
throw new JITEnrollmentException("Error occurred while executing token acquiring request", ex);
}
}
/***
* Build token acquire request
* @return {@link HttpPost} Token acquire request
*/
private HttpPost buildTokenAcquireRequest() {
HttpPost tokenAcquiringRequest = new HttpPost(keyManagerUrl + HandlerConstants.OAUTH2_TOKEN_ENDPOINT);
tokenAcquiringRequest.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString());
tokenAcquiringRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC
+ encodedClientCredentials);
StringEntity payload = new StringEntity(
"grant_type=" + HandlerConstants.CLIENT_CREDENTIAL_GRANT_TYPE + "&scope=" + scope,
ContentType.APPLICATION_FORM_URLENCODED);
tokenAcquiringRequest.setEntity(payload);
return tokenAcquiringRequest;
}
/***
* Persists auth data in session
* @param session - {@link HttpSession}
* @param token - Json object containing token data
*/
private void persistAuthData(HttpSession session, JsonObject token) {
AuthData authData = new AuthData();
authData.setAccessToken(token.get("access_token").getAsString());
authData.setClientId(clientId);
authData.setClientSecret(clientSecret);
authData.setEncodedClientApp(encodedClientCredentials);
authData.setScope(token.get("scope").getAsString());
session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData);
}
}

@ -0,0 +1,93 @@
/*
* 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.ui.request.interceptor;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITEnrollmentData;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITEnrollmentException;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.wso2.carbon.utils.CarbonUtils;
import org.xml.sax.SAXException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet(
name = "JIT enrollment handler",
description = "Handle jit enrollment request",
urlPatterns = {
"/jit-enrollment"
}
)
public class JITEnrollmentHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITEnrollmentHandler.class);
private String username;
private String ownershipType;
private String os;
private String redirectUrl;
private String tenantDomain;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
HttpSession session = request.getSession(true);
String JITProvisionHandlerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath()
+ HandlerConstants.JIT_PROVISION_HANDLER;
String onCompletionUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath()
+ "/jit-enrollment-callback";
username = request.getParameter("username");
ownershipType = request.getParameter("ownershipType");
os = request.getParameter("os");
redirectUrl = request.getParameter("redirectUrl");
tenantDomain = request.getParameter("tenantDomain");
String sp = request.getParameter("sp");
persistJITData(session);
response.sendRedirect(JITProvisionHandlerUrl + "?tenantDomain=" + tenantDomain
+ "&sp=" + sp + "&redirectUrl=" + onCompletionUrl);
} catch (IOException ex) {
log.error("Error occurred while handling JIT enrollment request");
}
}
/***
* Persists JIT data in session
* @param session - {@link HttpSession}
*/
private void persistJITData(HttpSession session) {
JITEnrollmentData JITEnrollmentInfo = new JITEnrollmentData();
JITEnrollmentInfo.setOwnershipType(ownershipType);
JITEnrollmentInfo.setOs(os);
JITEnrollmentInfo.setUsername(username);
JITEnrollmentInfo.setRedirectUrl(redirectUrl);
JITEnrollmentInfo.setTenantDomain(tenantDomain);
session.setAttribute(HandlerConstants.SESSION_JIT_ENROLLMENT_DATA_KEY, JITEnrollmentInfo);
}
}

@ -0,0 +1,80 @@
/*
* 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.ui.request.interceptor;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITEnrollmentData;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Objects;
@WebServlet(
name = "JIT callback handler",
description = "Call token endpoint and retrieve token",
urlPatterns = {
"/jit-provision-callback"
}
)
public class JITProvisionCallbackHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITProvisionCallbackHandler.class);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String state = request.getParameter("state");
HttpSession session = request.getSession(false);
String JITProvisionCallbackURL = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath()
+ HandlerConstants.JIT_PROVISION_CALLBACK_URL;
try {
if (session == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED);
return;
}
if (state == null || !Objects.equals(state, session.getAttribute("state").toString())) {
response.sendError(org.apache.http.HttpStatus.SC_BAD_REQUEST, "MismatchingStateError: CSRF Warning! " +
"State not equal in request and response");
return;
}
JITData JITInfo = (JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY);
if (JITInfo == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED);
return;
}
response.sendRedirect(JITInfo.getRedirectUrl() + "?code=" + request.getParameter("code")
+ "&redirectUrl=" + JITProvisionCallbackURL);
} catch (IOException ex) {
log.error("Error occurred while processing JIT provisioning callback request", ex);
}
}
}

@ -0,0 +1,198 @@
/*
* 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.ui.request.interceptor;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITEnrollmentException;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITProvisionException;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wso2.carbon.utils.CarbonUtils;
import org.xml.sax.SAXException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@WebServlet(
name = "JITProvisionRequestHandlerServlet",
description = "Handle Just In Time Provisioning requests",
urlPatterns = {
"/jit-provision"
}
)
public class JITProvisionHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITProvisionHandler.class);
private String tenantDomain;
private String clientId;
private String JITServiceProviderName;
private String encodedClientCredentials;
private String JITConfigurationPath;
private String redirectUrl;
private String state;
private static final Map<String, Element> tenantConfigs = new HashMap<>();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme());
String JITCallbackUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath()
+ HandlerConstants.JIT_PROVISION_CALLBACK_URL;
JITConfigurationPath = CarbonUtils.getCarbonConfigDirPath() + File.separator + "jit-config.xml";
String scope = "openid";
state = HandlerUtil.generateStateToken();
tenantDomain = request.getParameter("tenantDomain");
redirectUrl = request.getParameter("redirectUrl");
JITServiceProviderName = request.getParameter("sp");
try {
if (tenantDomain == null || JITServiceProviderName == null) {
HandlerUtil.handleError(response, HttpStatus.SC_BAD_REQUEST);
return;
}
if (!initializeJITConfigurations()) {
HandlerUtil.handleError(response, HttpStatus.SC_SERVICE_UNAVAILABLE);
return;
}
persistJITData(request.getSession(true));
response.sendRedirect(keyManagerUrl + HandlerConstants.AUTHORIZATION_ENDPOINT +
"?response_type=code" +
"&client_id=" + clientId +
"&state=" + state +
"&scope=" + scope +
"&redirect_uri=" + JITCallbackUrl);
} catch (JITProvisionException | IOException ex) {
log.error("Error occurred while processing JIT provisioning request", ex);
}
}
/***
* Retrieve JIT data from current session if session exists, otherwise build and return
* @param session - {@link HttpSession}
* @return {@link JITData}
*/
private JITData getJITData(HttpSession session) {
return (session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY) != null) ?
(JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY) : new JITData();
}
/***
* Persists JIT data in session
* @param session {@link HttpSession}
*/
private void persistJITData(HttpSession session) {
JITData JITInfo = getJITData(session);
JITInfo.setEncodedClientCredentials(encodedClientCredentials);
JITInfo.setTenantDomain(tenantDomain);
JITInfo.setRedirectUrl(redirectUrl);
JITInfo.setSp(JITServiceProviderName);
session.setMaxInactiveInterval(3600);
session.setAttribute("state", state);
session.setAttribute(HandlerConstants.SESSION_JIT_DATA_KEY, JITInfo);
}
/***
* Find the tenant based configurations and return
* @param tenantDomain - Domain of the tenant
* @param document - Config doc
* @return {@link Element} If config found return configuration element, otherwise null
*/
private Element findServiceProvider(String tenantDomain, Document document) {
NodeList serviceProviderConfiguration = document.getElementsByTagName("ServiceProvider");
for (int idx = 0; idx < serviceProviderConfiguration.getLength(); idx++) {
Node configNode = serviceProviderConfiguration.item(idx);
if (configNode.getNodeType() == Node.ELEMENT_NODE) {
Element configElement = (Element) configNode;
if (Objects.equals(configElement.getAttributes().
getNamedItem("tenantDomain").getNodeValue(), tenantDomain) &&
Objects.equals(configElement.getAttributes().getNamedItem("name").getNodeValue(),
JITServiceProviderName)) {
return configElement;
}
}
}
return null;
}
/***
* Initialize JIT configurations
* @return boolean true when successful initialization, otherwise false
* @throws JITProvisionException throws when error occurred
*/
private boolean initializeJITConfigurations() throws JITProvisionException {
try {
Element serviceProvider = tenantConfigs.get(tenantDomain);
if (serviceProvider == null) {
File JITConfigurationFile = new File(JITConfigurationPath);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document JITConfigurationDoc = documentBuilder.parse(JITConfigurationFile);
JITConfigurationDoc.getDocumentElement().normalize();
serviceProvider = findServiceProvider(tenantDomain, JITConfigurationDoc);
if (serviceProvider == null) return false;
tenantConfigs.put(tenantDomain, serviceProvider);
}
clientId = serviceProvider.getElementsByTagName("ClientId").item(0).getTextContent();
String clientSecret = serviceProvider.getElementsByTagName("ClientSecret").item(0).getTextContent();
String headerValue = clientId + ":" + clientSecret;
encodedClientCredentials = Base64.getEncoder().encodeToString(headerValue.getBytes());
return true;
} catch (ParserConfigurationException ex) {
String msg = "Error occurred when document builder creating the file configuration";
throw new JITProvisionException(msg, ex);
} catch (IOException ex) {
String msg = "IO error occurred while parsing the JIT config file";
throw new JITProvisionException(msg, ex);
} catch (SAXException ex) {
String msg = "Parse error occurred while parsing the JIT config document";
throw new JITProvisionException(msg, ex);
}
}
}

@ -0,0 +1,68 @@
/*
* Copyright (C) 2018 - 2023 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
*
* Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
*
* Licensed under the Entgra Commercial License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://entgra.io/licenses/entgra-commercial/1.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.ui.request.interceptor.beans;
public class JITData {
private String username;
private String tenantDomain;
private String redirectUrl;
private String sp;
private String encodedClientCredentials;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getTenantDomain() {
return tenantDomain;
}
public void setTenantDomain(String tenantDomain) {
this.tenantDomain = tenantDomain;
}
public String getRedirectUrl() {
return redirectUrl;
}
public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}
public String getSp() {
return sp;
}
public void setSp(String sp) {
this.sp = sp;
}
public String getEncodedClientCredentials() {
return encodedClientCredentials;
}
public void setEncodedClientCredentials(String encodedClientCredentials) {
this.encodedClientCredentials = encodedClientCredentials;
}
}

@ -0,0 +1,68 @@
/*
* Copyright (C) 2018 - 2023 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
*
* Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
*
* Licensed under the Entgra Commercial License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://entgra.io/licenses/entgra-commercial/1.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.ui.request.interceptor.beans;
public class JITEnrollmentData {
private String username;
private String tenantDomain;
private String ownershipType;
private String os;
private String redirectUrl;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getTenantDomain() {
return tenantDomain;
}
public void setTenantDomain(String tenantDomain) {
this.tenantDomain = tenantDomain;
}
public String getOwnershipType() {
return ownershipType;
}
public void setOwnershipType(String ownershipType) {
this.ownershipType = ownershipType;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public String getRedirectUrl() {
return redirectUrl;
}
public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}
}

@ -0,0 +1,30 @@
/*
* Copyright (C) 2018 - 2023 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
*
* Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
*
* Licensed under the Entgra Commercial License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://entgra.io/licenses/entgra-commercial/1.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.ui.request.interceptor.exceptions;
public class JITEnrollmentException extends Exception {
public JITEnrollmentException(String msg, Throwable t) {
super(msg, t);
}
public JITEnrollmentException(String msg) {
super(msg);
}
}

@ -0,0 +1,29 @@
/*
* Copyright (C) 2018 - 2023 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
*
* Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
*
* Licensed under the Entgra Commercial License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://entgra.io/licenses/entgra-commercial/1.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.ui.request.interceptor.exceptions;
public class JITProvisionException extends Exception {
public JITProvisionException(String msg, Throwable t) {
super(msg, t);
}
public JITProvisionException(String msg) {
super(msg);
}
}

@ -107,4 +107,18 @@ public class HandlerConstants {
public static final String USER_SCOPES = "userScopes";
public static final String HUBSPOT_CHAT_URL = "api.hubapi.com";
public static final String USERNAME_WITH_DOMAIN = "usernameWithDomain";
public static final String JIT_PROVISION_CALLBACK_URL = "/jit-provision-callback";
public static final String JIT_ENROLLMENT_HANDLER_CALLBACK_URL = "/jit-enrollment-callback";
public static final String DCR_URL = "/client-registration/v0.17/register";
public static final String SESSION_JIT_DATA_KEY = "JITInfo";
public static final String SESSION_JIT_ENROLLMENT_DATA_KEY = "JITEnrollmentInfo";
public static final String JIT_PROVISION_HANDLER = "/jit-provision";
public static final String JIT_ENROLLMENT_AUTH_APP_KEY = "JIT_ENROLLMENT_AUTH_APP";
public static final String CLIENT_CREDENTIAL_GRANT_TYPE = "client_credentials";
public static final String OS_ANDROID = "android";
public static final String OS_WINDOWS = "windows";
public static final String OS_IOS = "ios";
public static final String TAG_ANDROID_ENROLLMENT_SCOPES = "AndroidEnrollmentScopes";
public static final String TAG_WINDOWS_ENROLLMENT_SCOPES = "WindowsEnrollmentScopes";
public static final String TAG_IOS_ENROLLMENT_SCOPES = "IOSEnrollmentScopes";
}

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
~ 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.
-->
<JITConfiguration>
<EnrollmentConfiguration>
<AndroidEnrollmentScopes>
<Scope>dm:metadata:view</Scope>
<Scope>dm:metadata:create</Scope>
<Scope>dm:metadata:update</Scope>
<Scope>and:devices:enroll</Scope>
<Scope>dm:device:enroll</Scope>
<Scope>and:conf:view</Scope>
</AndroidEnrollmentScopes>
<IOSEnrollmentScopes>
<!-- <Scope></Scope> -->
</IOSEnrollmentScopes>
<WindowsEnrollmentScopes>
<!-- <Scope></Scope> -->
</WindowsEnrollmentScopes>
</EnrollmentConfiguration>
<ServiceProviderConfiguration>
<!--<ServiceProvider tenantDomain="" name="">
<ClientId></ClientId>
<ClientSecret></ClientSecret>
</ServiceProvider>-->
</ServiceProviderConfiguration>
</JITConfiguration>

@ -0,0 +1,61 @@
#*
Copyright (C) 2018 - 2023 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
Licensed under the Entgra Commercial License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://entgra.io/licenses/entgra-commercial/1.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>You have been invited to enroll your $device-type device in Entgra IoT</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 $username,
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
You have been invited by $invite-by to enrol your $device-type device in Entgra IoT Server.
Click <a href="$base-url-https/enroll-web-agent/$device-type/provision?username=$username&amp;sp=$sp&amp;tenantDomain=$tenant-domain&amp;ownershipType=$ownership-type&amp;os=$device-type">here</a> to begin device
enrolment.</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>
]]>
</Body>
</EmailConfig>

@ -13,3 +13,4 @@ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../../r
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.device.mgt.basics_${feature.version}/email/templates,target:${installFolder}/../../../repository/resources/email-templates,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.device.mgt.basics_${feature.version}/conf_templates/,target:${installFolder}/../../resources/conf/,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.device.mgt.basics_${feature.version}/conf/traccar-config.xml,target:${installFolder}/../../conf/traccar-config.xml,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.device.mgt.basics_${feature.version}/conf/jit-config.xml,target:${installFolder}/../../conf/jit-config.xml,overwrite:true);\

@ -4,4 +4,5 @@ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../featur
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/store-ui-request-handler.war,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/entgra-ui-request-handler.war,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/mdm-reports-ui-request-handler.war,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/enroll-web-agent-ui-request-handler.war,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.device.mgt.core.ui.request.interceptor_${feature.version}/payloads/,target:${installFolder}/../../resources/payloads/,overwrite:true);\

Loading…
Cancel
Save