diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/InvitationMailProfile.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/InvitationMailProfile.java new file mode 100644 index 0000000000..4133d3f25e --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/InvitationMailProfile.java @@ -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; + } +} diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/JITEnrollmentInvitation.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/JITEnrollmentInvitation.java new file mode 100644 index 0000000000..32e4bfbe9d --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/JITEnrollmentInvitation.java @@ -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 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 getMailProfiles() { + return mailProfiles; + } + + public void setMailProfiles(List 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; + } +} diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/api/UserManagementService.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/api/UserManagementService.java index 2b7b81b72f..2f824ef34b 100644 --- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/api/UserManagementService.java +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/api/UserManagementService.java @@ -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); diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java index cd8063fdca..bc3a26b88d 100644 --- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java @@ -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 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 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 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 * diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITEnrollmentCallbackHandler.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITEnrollmentCallbackHandler.java new file mode 100644 index 0000000000..6a82ae3f26 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITEnrollmentCallbackHandler.java @@ -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); + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITEnrollmentHandler.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITEnrollmentHandler.java new file mode 100644 index 0000000000..8ca9cdf98c --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITEnrollmentHandler.java @@ -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); + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITProvisionCallbackHandler.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITProvisionCallbackHandler.java new file mode 100644 index 0000000000..433c1e7de3 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITProvisionCallbackHandler.java @@ -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); + } + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITProvisionHandler.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITProvisionHandler.java new file mode 100644 index 0000000000..9c1cf31a94 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/JITProvisionHandler.java @@ -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 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); + } + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/beans/JITData.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/beans/JITData.java new file mode 100644 index 0000000000..0e5f1769e7 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/beans/JITData.java @@ -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; + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/beans/JITEnrollmentData.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/beans/JITEnrollmentData.java new file mode 100644 index 0000000000..225d31cc91 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/beans/JITEnrollmentData.java @@ -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; + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/exceptions/JITEnrollmentException.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/exceptions/JITEnrollmentException.java new file mode 100644 index 0000000000..c845f8b5c8 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/exceptions/JITEnrollmentException.java @@ -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); + } +} + diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/exceptions/JITProvisionException.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/exceptions/JITProvisionException.java new file mode 100644 index 0000000000..fc3018890d --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/exceptions/JITProvisionException.java @@ -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); + } +} diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerConstants.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerConstants.java index 9aa9cb9fe6..44aedaafe5 100644 --- a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerConstants.java +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerConstants.java @@ -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"; } diff --git a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf/jit-config.xml b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf/jit-config.xml new file mode 100644 index 0000000000..1b72287a08 --- /dev/null +++ b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf/jit-config.xml @@ -0,0 +1,44 @@ + + + + + + + + dm:metadata:view + dm:metadata:create + dm:metadata:update + and:devices:enroll + dm:device:enroll + and:conf:view + + + + + + + + + + + + \ No newline at end of file diff --git a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/email/templates/default-jit-enrollment-invitation.vm b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/email/templates/default-jit-enrollment-invitation.vm new file mode 100644 index 0000000000..4487662397 --- /dev/null +++ b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/email/templates/default-jit-enrollment-invitation.vm @@ -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. +*# + + You have been invited to enroll your $device-type device in Entgra IoT + + + + Entgra IoT Server + + +
+
+
+
+ entgra +
+
+
+

+ Hi $username, +

+

+ You have been invited by $invite-by to enrol your $device-type device in Entgra IoT Server. + Click here to begin device + enrolment.

+ +

+ Should you need assistance, please contact your administrator. +

+ +

+ Regards, +

+

+ Entgra IoT Administrator +

+
+
+
+ + + ]]> + +
\ No newline at end of file diff --git a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/p2.inf b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/p2.inf index e861fe0e56..80897977ed 100644 --- a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/p2.inf +++ b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/p2.inf @@ -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);\ diff --git a/features/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor.feature/src/main/resources/p2.inf b/features/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor.feature/src/main/resources/p2.inf index df48e94eb2..1e33a85fad 100644 --- a/features/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor.feature/src/main/resources/p2.inf +++ b/features/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor.feature/src/main/resources/p2.inf @@ -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);\