Merge branch 'tenant-improve' into 'master'

Add OTP service and it's features

See merge request entgra/carbon-device-mgt!624
revert-70ac1926
Inosh Perara 4 years ago
commit fcd92bc10e

@ -232,4 +232,4 @@ public class AuthenticationHandler extends AbstractHandler {
map.put(CONTENT_TYPE, "application/json"); map.put(CONTENT_TYPE, "application/json");
return map; return map;
} }
} }

@ -24,6 +24,7 @@ public class AuthConstants {
public static final String MDM_SIGNATURE = "mdm-signature"; public static final String MDM_SIGNATURE = "mdm-signature";
public static final String PROXY_MUTUAL_AUTH_HEADER = "proxy-mutual-auth-header"; public static final String PROXY_MUTUAL_AUTH_HEADER = "proxy-mutual-auth-header";
public static final String MUTUAL_AUTH_HEADER = "mutual-auth-header"; public static final String MUTUAL_AUTH_HEADER = "mutual-auth-header";
public static final String ONE_TIME_TOKEN_HEADER = "one-time-token";
public static final String ENCODED_PEM = "encoded-pem"; public static final String ENCODED_PEM = "encoded-pem";
public static final String CALLBACK_URL = ""; public static final String CALLBACK_URL = "";
public static final String CLIENT_NAME = "IOT-API-MANAGER"; public static final String CLIENT_NAME = "IOT-API-MANAGER";

@ -273,5 +273,9 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.wso2.carbon.multitenancy</groupId>
<artifactId>org.wso2.carbon.tenant.mgt</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

@ -124,4 +124,4 @@
<url-pattern>/*</url-pattern> <url-pattern>/*</url-pattern>
</filter-mapping> </filter-mapping>
</web-app> </web-app>

@ -35,7 +35,6 @@
package org.wso2.carbon.device.mgt.jaxrs.service.api; package org.wso2.carbon.device.mgt.jaxrs.service.api;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.swagger.annotations.SwaggerDefinition; import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Info; import io.swagger.annotations.Info;
import io.swagger.annotations.ExtensionProperty; import io.swagger.annotations.ExtensionProperty;
@ -50,6 +49,7 @@ import io.swagger.annotations.ResponseHeader;
import org.apache.axis2.transport.http.HTTPConstants; import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.carbon.apimgt.annotations.api.Scopes; import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import org.wso2.carbon.device.mgt.jaxrs.beans.ActivityList; import org.wso2.carbon.device.mgt.jaxrs.beans.ActivityList;
import org.wso2.carbon.device.mgt.jaxrs.beans.BasicUserInfo; import org.wso2.carbon.device.mgt.jaxrs.beans.BasicUserInfo;
import org.wso2.carbon.device.mgt.jaxrs.beans.BasicUserInfoList; import org.wso2.carbon.device.mgt.jaxrs.beans.BasicUserInfoList;
@ -75,7 +75,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.List;
@SwaggerDefinition( @SwaggerDefinition(
info = @Info( info = @Info(
@ -894,7 +893,8 @@ public interface UserManagementService {
@ApiParam( @ApiParam(
name = "users", name = "users",
value = "List of users", value = "List of users",
required = true) List<String> usernames); required = true)
@Valid DeviceEnrollmentInvitation deviceEnrollmentInvitation);
@POST @POST
@Path("/enrollment-invite") @Path("/enrollment-invite")

@ -1288,6 +1288,10 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
operation.setId(operationStatusBean.getOperationId()); operation.setId(operationStatusBean.getOperationId());
DeviceMgtAPIUtils.getDeviceManagementService().updateOperation(device, operation); DeviceMgtAPIUtils.getDeviceManagementService().updateOperation(device, operation);
return Response.status(Response.Status.OK).entity("OperationStatus updated successfully.").build(); return Response.status(Response.Status.OK).entity("OperationStatus updated successfully.").build();
} catch (BadRequestException e) {
String msg = "Error occured due to invalid request";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (DeviceManagementException e) { } catch (DeviceManagementException e) {
String msg = "Error occurred when fetching device " + deviceIdentifier.toString(); String msg = "Error occurred when fetching device " + deviceIdentifier.toString();
log.error(msg, e); log.error(msg, e);
@ -1296,10 +1300,6 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
String msg = "Error occurred when updating operation of device " + deviceIdentifier; String msg = "Error occurred when updating operation of device " + deviceIdentifier;
log.error(msg, e); log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch (BadRequestException e) {
String msg = "Error occured due to invalid request";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} }
} }

@ -45,8 +45,11 @@ import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.OTPManagementException;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.service.EmailMetaInfo; import org.wso2.carbon.device.mgt.core.service.EmailMetaInfo;
@ -716,48 +719,29 @@ public class UserManagementServiceImpl implements UserManagementService {
return CredentialManagementResponseBuilder.buildChangePasswordResponse(credentials); return CredentialManagementResponseBuilder.buildChangePasswordResponse(credentials);
} }
/**
* Method used to send an invitation email to a existing user to enroll a device.
*
* @param usernames Username list of the users to be invited
*/
@POST @POST
@Path("/send-invitation") @Path("/send-invitation")
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public Response inviteExistingUsersToEnrollDevice(List<String> usernames) { public Response inviteExistingUsersToEnrollDevice(DeviceEnrollmentInvitation deviceEnrollmentInvitation) {
if (deviceEnrollmentInvitation.getUsernames() == null || deviceEnrollmentInvitation.getUsernames().isEmpty()) {
String msg = "Error occurred while validating list of user-names. User-names cannot be empty.";
log.error(msg);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setCode(HttpStatus.SC_BAD_REQUEST).setMessage(msg)
.build());
}
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Sending enrollment invitation mail to existing user."); log.debug("Sending device enrollment invitation mail to existing user/s.");
} }
DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService(); OTPManagementService oms = DeviceMgtAPIUtils.getOTPManagementService();
try { try {
for (String username : usernames) { oms.sendDeviceEnrollmentInvitationMail(deviceEnrollmentInvitation);
String recipient = getClaimValue(username, Constants.USER_CLAIM_EMAIL_ADDRESS); } catch (OTPManagementException e) {
String msg = "Error occurred while generating OTP and inviting user/s to enroll their device/s.";
Properties props = new Properties();
props.setProperty("first-name", getClaimValue(username, Constants.USER_CLAIM_FIRST_NAME));
props.setProperty("username", username);
EmailMetaInfo metaInfo = new EmailMetaInfo(recipient, props);
dms.sendEnrolmentInvitation(DeviceManagementConstants.EmailAttributes.USER_ENROLLMENT_TEMPLATE,
metaInfo);
}
} catch (DeviceManagementException e) {
String msg = "Error occurred while inviting user to enrol their device";
if (e.getMessage() != null && !e.getMessage().isEmpty()) {
msg = e.getMessage();
}
log.error(msg, e); log.error(msg, e);
return Response.serverError().entity( return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (UserStoreException e) {
String msg = "Error occurred while getting claim values to invite user";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (ConfigurationManagementException e) {
String msg = "Error occurred while sending the email invitations. Mail server not configured.";
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} }
return Response.status(Response.Status.OK).entity("Invitation mails have been sent.").build(); return Response.status(Response.Status.OK).entity("Invitation mails have been sent.").build();
} }

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*
*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ */
package org.wso2.carbon.device.mgt.jaxrs.util; package org.wso2.carbon.device.mgt.jaxrs.util;
@ -52,6 +69,7 @@ import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.permission.mgt.PermissionManagerService; import org.wso2.carbon.device.mgt.common.permission.mgt.PermissionManagerService;
import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService; import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService;
import org.wso2.carbon.device.mgt.common.spi.DeviceTypeGeneratorService; import org.wso2.carbon.device.mgt.common.spi.DeviceTypeGeneratorService;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService; import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService;
import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager;
import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion;
@ -134,13 +152,14 @@ public class DeviceMgtAPIUtils {
public static final String DAS_ADMIN_SERVICE_EP = "https://" + DAS_HOST_NAME + ":" + DAS_PORT + "/services/"; public static final String DAS_ADMIN_SERVICE_EP = "https://" + DAS_HOST_NAME + ":" + DAS_PORT + "/services/";
private static SSLContext sslContext; private static SSLContext sslContext;
private static Log log = LogFactory.getLog(DeviceMgtAPIUtils.class); private static final Log log = LogFactory.getLog(DeviceMgtAPIUtils.class);
private static KeyStore keyStore; private static KeyStore keyStore;
private static KeyStore trustStore; private static KeyStore trustStore;
private static char[] keyStorePassword; private static char[] keyStorePassword;
private static IntegrationClientService integrationClientService; private static IntegrationClientService integrationClientService;
private static MetadataManagementService metadataManagementService; private static MetadataManagementService metadataManagementService;
private static OTPManagementService otpManagementService;
static { static {
String keyStorePassword = ServerConfiguration.getInstance().getFirstProperty("Security.KeyStore.Password"); String keyStorePassword = ServerConfiguration.getInstance().getFirstProperty("Security.KeyStore.Password");
@ -337,6 +356,25 @@ public class DeviceMgtAPIUtils {
return integrationClientService; return integrationClientService;
} }
/**
* Initializing and accessing method for OTPManagementService.
*
* @return OTPManagementService instance
* @throws IllegalStateException if OTPManagementService cannot be initialized
*/
public static synchronized OTPManagementService getOTPManagementService() {
if (otpManagementService == null) {
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
otpManagementService = (OTPManagementService) ctx.getOSGiService(OTPManagementService.class, null);
if (otpManagementService == null) {
String msg = "OTP Management service has not initialized.";
log.error(msg);
throw new IllegalStateException(msg);
}
}
return otpManagementService;
}
public static RegistryService getRegistryService() { public static RegistryService getRegistryService() {
RegistryService registryService; RegistryService registryService;
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();

@ -15,6 +15,23 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
* *
*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/ */
package org.wso2.carbon.device.mgt.jaxrs.service.impl; package org.wso2.carbon.device.mgt.jaxrs.service.impl;
@ -34,6 +51,10 @@ import org.testng.annotations.Test;
import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.OTPManagementException;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.device.mgt.core.otp.mgt.service.OTPManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderServiceImpl; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderServiceImpl;
import org.wso2.carbon.device.mgt.jaxrs.beans.BasicUserInfo; import org.wso2.carbon.device.mgt.jaxrs.beans.BasicUserInfo;
@ -66,9 +87,11 @@ public class UserManagementServiceImplTest {
private UserStoreManager userStoreManager; private UserStoreManager userStoreManager;
private UserManagementService userManagementService; private UserManagementService userManagementService;
private DeviceManagementProviderService deviceManagementProviderService; private DeviceManagementProviderService deviceManagementProviderService;
private OTPManagementService otpManagementService;
private static final String DEFAULT_DEVICE_USER = "Internal/devicemgt-user"; private static final String DEFAULT_DEVICE_USER = "Internal/devicemgt-user";
private UserRealm userRealm; private UserRealm userRealm;
private EnrollmentInvitation enrollmentInvitation; private EnrollmentInvitation enrollmentInvitation;
private DeviceEnrollmentInvitation deviceEnrollmentInvitation;
private List<String> userList; private List<String> userList;
private static final String TEST_USERNAME = "test"; private static final String TEST_USERNAME = "test";
private static final String TEST2_USERNAME = "test2"; private static final String TEST2_USERNAME = "test2";
@ -86,6 +109,7 @@ public class UserManagementServiceImplTest {
userStoreManager = Mockito.mock(UserStoreManager.class, Mockito.RETURNS_MOCKS); userStoreManager = Mockito.mock(UserStoreManager.class, Mockito.RETURNS_MOCKS);
deviceManagementProviderService = Mockito deviceManagementProviderService = Mockito
.mock(DeviceManagementProviderServiceImpl.class, Mockito.CALLS_REAL_METHODS); .mock(DeviceManagementProviderServiceImpl.class, Mockito.CALLS_REAL_METHODS);
otpManagementService = Mockito.mock(OTPManagementServiceImpl.class, Mockito.CALLS_REAL_METHODS);
userRealm = Mockito.mock(UserRealm.class); userRealm = Mockito.mock(UserRealm.class);
RealmConfiguration realmConfiguration = Mockito.mock(RealmConfiguration.class); RealmConfiguration realmConfiguration = Mockito.mock(RealmConfiguration.class);
Mockito.doReturn(null).when(realmConfiguration).getSecondaryRealmConfig(); Mockito.doReturn(null).when(realmConfiguration).getSecondaryRealmConfig();
@ -97,6 +121,8 @@ public class UserManagementServiceImplTest {
enrollmentInvitation.setRecipients(recipients); enrollmentInvitation.setRecipients(recipients);
userList = new ArrayList<>(); userList = new ArrayList<>();
userList.add(TEST_USERNAME); userList.add(TEST_USERNAME);
deviceEnrollmentInvitation = new DeviceEnrollmentInvitation();
deviceEnrollmentInvitation.setUsernames(userList);
} }
@Test(description = "This method tests the addUser method of UserManagementService") @Test(description = "This method tests the addUser method of UserManagementService")
@ -205,13 +231,11 @@ public class UserManagementServiceImplTest {
@Test(description = "This method tests the send invitation method of UserManagementService", dependsOnMethods = @Test(description = "This method tests the send invitation method of UserManagementService", dependsOnMethods =
{"testIsUserExists"}) {"testIsUserExists"})
public void testSendInvitation() throws ConfigurationManagementException, DeviceManagementException { public void testSendInvitation() throws OTPManagementException {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getUserStoreManager")) PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getOTPManagementService"))
.toReturn(this.userStoreManager); .toReturn(this.otpManagementService);
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService")) Mockito.doNothing().when(otpManagementService).sendDeviceEnrollmentInvitationMail(Mockito.any());
.toReturn(this.deviceManagementProviderService); Response response = userManagementService.inviteExistingUsersToEnrollDevice(deviceEnrollmentInvitation);
Mockito.doNothing().when(deviceManagementProviderService).sendEnrolmentInvitation(Mockito.any(), Mockito.any());
Response response = userManagementService.inviteExistingUsersToEnrollDevice(userList);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(),
"Inviting existing users to enroll device failed"); "Inviting existing users to enroll device failed");
} }
@ -240,7 +264,7 @@ public class UserManagementServiceImplTest {
@Test(description = "This method tests the inviteToEnrollDevice method of UserManagementService", @Test(description = "This method tests the inviteToEnrollDevice method of UserManagementService",
dependsOnMethods = "testGetUsers") dependsOnMethods = "testGetUsers")
public void testInviteToEnrollDevice() { public void testInviteToEnrollDevice() throws ConfigurationManagementException, DeviceManagementException {
URL resourceUrl = ClassLoader.getSystemResource("testng.xml"); URL resourceUrl = ClassLoader.getSystemResource("testng.xml");
System.setProperty("carbon.home", resourceUrl.getPath()); System.setProperty("carbon.home", resourceUrl.getPath());
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getUserStoreManager")) PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getUserStoreManager"))
@ -248,6 +272,7 @@ public class UserManagementServiceImplTest {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getAuthenticatedUser")).toReturn(TEST_USERNAME); PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getAuthenticatedUser")).toReturn(TEST_USERNAME);
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService")) PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService"))
.toReturn(this.deviceManagementProviderService); .toReturn(this.deviceManagementProviderService);
Mockito.doNothing().when(deviceManagementProviderService).sendEnrolmentInvitation(Mockito.any(), Mockito.any());
EnrollmentInvitation enrollmentInvitation = new EnrollmentInvitation(); EnrollmentInvitation enrollmentInvitation = new EnrollmentInvitation();
List<String> recipients = new ArrayList<>(); List<String> recipients = new ArrayList<>();
recipients.add(TEST_USERNAME); recipients.add(TEST_USERNAME);
@ -289,16 +314,22 @@ public class UserManagementServiceImplTest {
@Test(description = "This method tests the behaviour of methods when there is an issue with " @Test(description = "This method tests the behaviour of methods when there is an issue with "
+ "DeviceManagementProviderService", dependsOnMethods = {"testGetUserCount"}) + "DeviceManagementProviderService", dependsOnMethods = {"testGetUserCount"})
public void testNegativeScenarios1() throws ConfigurationManagementException, DeviceManagementException { public void testNegativeScenarios1()
throws ConfigurationManagementException, DeviceManagementException, OTPManagementException {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getUserStoreManager")) PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getUserStoreManager"))
.toReturn(this.userStoreManager); .toReturn(this.userStoreManager);
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService")) PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService"))
.toReturn(this.deviceManagementProviderService); .toReturn(this.deviceManagementProviderService);
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getAuthenticatedUser")).toReturn(TEST_USERNAME); PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getAuthenticatedUser")).toReturn(TEST_USERNAME);
Mockito.reset(deviceManagementProviderService); Mockito.reset(deviceManagementProviderService);
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getOTPManagementService"))
.toReturn(this.otpManagementService);
Mockito.reset(otpManagementService);
Mockito.doThrow(new DeviceManagementException()).when(deviceManagementProviderService) Mockito.doThrow(new DeviceManagementException()).when(deviceManagementProviderService)
.sendEnrolmentInvitation(Mockito.any(), Mockito.any()); .sendEnrolmentInvitation(Mockito.any(), Mockito.any());
Response response = userManagementService.inviteExistingUsersToEnrollDevice(userList); Mockito.doThrow(new OTPManagementException()).when(otpManagementService)
.sendDeviceEnrollmentInvitationMail(Mockito.any());
Response response = userManagementService.inviteExistingUsersToEnrollDevice(deviceEnrollmentInvitation);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"Invite existing users to enroll device succeeded under erroneous conditions"); "Invite existing users to enroll device succeeded under erroneous conditions");
response = userManagementService.inviteToEnrollDevice(enrollmentInvitation); response = userManagementService.inviteToEnrollDevice(enrollmentInvitation);
@ -346,6 +377,8 @@ public class UserManagementServiceImplTest {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService")) PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService"))
.toReturn(this.deviceManagementProviderService); .toReturn(this.deviceManagementProviderService);
Mockito.reset(this.userStoreManager); Mockito.reset(this.userStoreManager);
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getOTPManagementService"))
.toReturn(this.otpManagementService);
Mockito.doThrow(new UserStoreException()).when(userStoreManager) Mockito.doThrow(new UserStoreException()).when(userStoreManager)
.getUserClaimValue(Mockito.any(), Mockito.any(), Mockito.any()); .getUserClaimValue(Mockito.any(), Mockito.any(), Mockito.any());
Mockito.doThrow(new UserStoreException()).when(userStoreManager) Mockito.doThrow(new UserStoreException()).when(userStoreManager)
@ -362,7 +395,7 @@ public class UserManagementServiceImplTest {
response = userManagementService.inviteToEnrollDevice(enrollmentInvitation); response = userManagementService.inviteToEnrollDevice(enrollmentInvitation);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"Invite existing users to enroll device succeeded under erroneous conditions"); "Invite existing users to enroll device succeeded under erroneous conditions");
response = userManagementService.inviteExistingUsersToEnrollDevice(userList); response = userManagementService.inviteExistingUsersToEnrollDevice(deviceEnrollmentInvitation);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"Invite existing users to enroll device succeeded under erroneous conditions"); "Invite existing users to enroll device succeeded under erroneous conditions");
} }

@ -125,4 +125,14 @@ public final class DeviceManagementConstants {
public static final String DEFAULT_HTTP_PROTOCOL = "https"; public static final String DEFAULT_HTTP_PROTOCOL = "https";
public static final String DAS_URL = DEFAULT_HTTP_PROTOCOL + "://" + DAS_HOST_NAME + ":" + DAS_PORT; public static final String DAS_URL = DEFAULT_HTTP_PROTOCOL + "://" + DAS_HOST_NAME + ":" + DAS_PORT;
} }
public static final class OTPProperties {
private OTPProperties() { throw new AssertionError(); }
public static final String FIRST_NAME = "first-name";
public static final String LAST_NAME = "last-name";
public static final String TENANT_ADMIN_USERNAME = "tenant-admin-username";
public static final String TENANT_ADMIN_PASSWORD = "tenant-admin-password";
}
} }

@ -19,7 +19,7 @@
package org.wso2.carbon.device.mgt.common.exceptions; package org.wso2.carbon.device.mgt.common.exceptions;
public class BadRequestException extends Exception { public class BadRequestException extends DeviceManagementException {
private static final long serialVersionUID = 2304023531260840549L; private static final long serialVersionUID = 2304023531260840549L;
public BadRequestException() { public BadRequestException() {

@ -0,0 +1,33 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.exceptions;
/**
* Exception thrown due to Database Connection issues.
*/
public class DBConnectionException extends Exception {
private static final long serialVersionUID = -6779125067467878014L;
public DBConnectionException(String message, Throwable cause) {
super(message, cause);
}
public DBConnectionException(String msg) {
super(msg);
}
}

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* you may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.exceptions;
public class OTPManagementException extends Exception {
private static final long serialVersionUID = 397485329551276175L;
public OTPManagementException(String msg, Exception nestedEx) {
super(msg, nestedEx);
}
public OTPManagementException(String message, Throwable cause) {
super(message, cause);
}
public OTPManagementException(String msg) {
super(msg);
}
public OTPManagementException() {
super();
}
public OTPManagementException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.wso2.carbon.device.mgt.common.exceptions;
public class UnAuthorizedException extends DeviceManagementException {
private static final long serialVersionUID = 2304023531260840549L;
public UnAuthorizedException() {
super();
}
public UnAuthorizedException(String msg) {
super(msg);
}
public UnAuthorizedException(Throwable cause) {
super(cause);
}
public UnAuthorizedException(String msg, Exception nestedEx) {
super(msg, nestedEx);
}
public UnAuthorizedException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.general;
import java.util.Map;
public class OneTimeTokenDetails extends TenantDetail {
String password;
String token;
long createdDate;
long updatedDate;
boolean isExpired;
String metaInfo;
Map<String, String> replaceValue;
String emailType;
public long getCreatedDate() {
return createdDate;
}
public void setCreatedDate(long createdDate) {
this.createdDate = createdDate;
}
public long getUpdatedDate() {
return updatedDate;
}
public void setUpdatedDate(long updatedDate) {
this.updatedDate = updatedDate;
}
public boolean isExpired() {
return isExpired;
}
public void setExpired(boolean expired) {
isExpired = expired;
}
public String getMetaInfo() {
return metaInfo;
}
public void setMetaInfo(String metaInfo) {
this.metaInfo = metaInfo;
}
public Map<String, String> getReplaceValue() {
return replaceValue;
}
public void setReplaceValue(Map<String, String> replaceValue) {
this.replaceValue = replaceValue;
}
public String getEmailType() {
return emailType;
}
public void setEmailType(String emailType) {
this.emailType = emailType;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}

@ -0,0 +1,59 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.invitation.mgt;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
@ApiModel(
value = "DeviceEnrollmentInvitation",
description = "Holds data to send device enrollment invitation to list of existing users.")
public class DeviceEnrollmentInvitation implements Serializable {
private static final long serialVersionUID = 6933837278652532052L;
@ApiModelProperty(
name = "usernames",
value = "List of usernames of users.",
required = true)
private List<String> usernames;
@ApiModelProperty(
name = "deviceEnrollmentTypes",
value = "List of enrollment types against device types.")
private List<DeviceEnrollmentType> deviceEnrollmentTypes;
public List<String> getUsernames() {
return usernames;
}
public void setUsernames(List<String> usernames) {
this.usernames = usernames;
}
public List<DeviceEnrollmentType> getDeviceEnrollmentTypes() {
return deviceEnrollmentTypes;
}
public void setDeviceEnrollmentTypes(
List<DeviceEnrollmentType> deviceEnrollmentTypes) {
this.deviceEnrollmentTypes = deviceEnrollmentTypes;
}
}

@ -0,0 +1,25 @@
package org.wso2.carbon.device.mgt.common.invitation.mgt;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import java.util.List;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "DeviceEnrollmentInvitationDetails", propOrder = {
"enrollmentDetails"
})
public class DeviceEnrollmentInvitationDetails {
@XmlElement(name = "EnrollmentDetails")
private List<EnrollmentDetails> enrollmentDetails;
public List<EnrollmentDetails> getEnrollmentDetails() {
return enrollmentDetails;
}
public void setEnrollmentDetails(List<EnrollmentDetails> enrollmentDetails) {
this.enrollmentDetails = enrollmentDetails;
}
}

@ -0,0 +1,59 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.invitation.mgt;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
@ApiModel(
value = "DeviceEnrollmentType",
description = "Holds data of enrollment types against device types.")
public class DeviceEnrollmentType implements Serializable {
private static final long serialVersionUID = 6563596191450032613L;
@ApiModelProperty(
name = "deviceType",
value = "Device type (i.e: android, ios, windows)",
required = true)
private String deviceType;
@ApiModelProperty(
name = "enrollmentType",
value = "Enrollment type (i.e: BYOD, COPE, COSU)",
required = true)
private List<String> enrollmentType;
public String getDeviceType() {
return deviceType;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public List<String> getEnrollmentType() {
return enrollmentType;
}
public void setEnrollmentType(List<String> enrollmentType) {
this.enrollmentType = enrollmentType;
}
}

@ -0,0 +1,36 @@
package org.wso2.carbon.device.mgt.common.invitation.mgt;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "EnrollmentDetails", propOrder = {
"enrollmentType",
"enrollmentSteps"
})
public class EnrollmentDetails {
@XmlElement(name = "EnrollmentType")
private String enrollmentType;
@XmlElement(name = "EnrollmentSteps")
private String enrollmentSteps;
public String getEnrollmentType() {
return enrollmentType;
}
public void setEnrollmentType(String enrollmentType) {
this.enrollmentType = enrollmentType;
}
public String getEnrollmentSteps() {
return enrollmentSteps;
}
public void setEnrollmentSteps(String enrollmentSteps) {
this.enrollmentSteps = enrollmentSteps;
}
}

@ -0,0 +1,22 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.otp.mgt;
public enum OTPEmailTypes {
USER_VERIFY, DEVICE_ENROLLMENT
}

@ -0,0 +1,113 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.otp.mgt.dto;
import java.sql.Timestamp;
public class OneTimePinDTO {
int id;
String otpToken;
int tenantId;
String username;
String email;
String emailType;
String metaInfo;
Timestamp createdAt;
int expiryTime;
boolean isExpired;
public int getTenantId() {
return tenantId;
}
public void setTenantId(int tenantId) {
this.tenantId = tenantId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getOtpToken() {
return otpToken;
}
public void setOtpToken(String otpToken) {
this.otpToken = otpToken;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmailType() {
return emailType;
}
public void setEmailType(String emailType) {
this.emailType = emailType;
}
public String getMetaInfo() { return metaInfo; }
public void setMetaInfo(String metaInfo) {
this.metaInfo = metaInfo;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}
public int getExpiryTime() {
return expiryTime;
}
public void setExpiryTime(int expiryTime) {
this.expiryTime = expiryTime;
}
public boolean isExpired() {
return isExpired;
}
public void setExpired(boolean expired) {
isExpired = expired;
}
}

@ -0,0 +1,53 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.otp.mgt.wrapper;
import org.wso2.carbon.device.mgt.common.metadata.mgt.Metadata;
import java.util.List;
public class OTPWrapper {
private String email;
private String emailType;
private String username;
private List<Metadata> properties;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmailType() {
return emailType;
}
public void setEmailType(String emailType) {
this.emailType = emailType;
}
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public List<Metadata> getProperties() { return properties; }
public void setProperties(List<Metadata> properties) { this.properties = properties; }
}

@ -38,6 +38,7 @@ import org.wso2.carbon.device.mgt.common.*;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager; import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.general.GeneralConfig; import org.wso2.carbon.device.mgt.common.general.GeneralConfig;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager;
import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber;
import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig; import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig;
@ -77,4 +78,6 @@ public interface DeviceManagementService {
DeviceTypePlatformDetails getDeviceTypePlatformDetails(); DeviceTypePlatformDetails getDeviceTypePlatformDetails();
DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails();
} }

@ -0,0 +1,65 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.common.spi;
import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.OTPManagementException;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import org.wso2.carbon.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import org.wso2.carbon.device.mgt.common.otp.mgt.wrapper.OTPWrapper;
import java.util.Map;
public interface OTPManagementService {
/**
* Create OTP token and store tenant details in the DB
* @param otpWrapper OTP Mail Wrapper object which contains tenant details of registering user
* @throws OTPManagementException if error occurs while creating OTP token and storing tenant details.
* @throws BadRequestException if found and incompatible payload to create OTP token.
*/
void sendUserVerifyingMail(OTPWrapper otpWrapper) throws OTPManagementException, DeviceManagementException;
/**
* Check the validity of the OTP
* @param oneTimeToken OTP
* @return The OTP data
* @throws OTPManagementException if error occurred whle verifying validity of the OPT
* @throws BadRequestException if found an null value for OTP
*/
OneTimePinDTO isValidOTP(String oneTimeToken) throws OTPManagementException, BadRequestException;
/**
* Invalidate the OTP and send welcome mail
* @param oneTimeToken OTP
* @param email email address
* @param properties email properties to add to email body
* @throws OTPManagementException if error occurred while invalidate the OTP or send welcome email
*/
void completeSelfRegistration(String oneTimeToken, String email, Map<String, String> properties)
throws OTPManagementException;
/**
* Create OTP token and send device enrollment invitation
* @param deviceEnrollmentInvitation object which contains device enrollment invitation related details
* @throws OTPManagementException if error occurred while creating OTP token &/ sending mail
*/
void sendDeviceEnrollmentInvitationMail(DeviceEnrollmentInvitation deviceEnrollmentInvitation)
throws OTPManagementException;
}

@ -360,6 +360,10 @@
<groupId>org.wso2.carbon.multitenancy</groupId> <groupId>org.wso2.carbon.multitenancy</groupId>
<artifactId>org.wso2.carbon.tenant.mgt</artifactId> <artifactId>org.wso2.carbon.tenant.mgt</artifactId>
</dependency> </dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

@ -116,6 +116,8 @@ public final class DeviceManagementConstants {
public static final String USER_REGISTRATION_TEMPLATE = "user-registration"; public static final String USER_REGISTRATION_TEMPLATE = "user-registration";
public static final String USER_ENROLLMENT_TEMPLATE = "user-enrollment"; public static final String USER_ENROLLMENT_TEMPLATE = "user-enrollment";
public static final String USER_VERIFY_TEMPLATE = "user-verify";
public static final String USER_WELCOME_TEMPLATE = "user-welcome";
public static final String DEFAULT_ENROLLMENT_TEMPLATE = "default-enrollment-invitation"; public static final String DEFAULT_ENROLLMENT_TEMPLATE = "default-enrollment-invitation";
} }
@ -142,6 +144,9 @@ public final class DeviceManagementConstants {
public static final String DEFAULT_DEVICE_USER = "Internal/devicemgt-user"; public static final String DEFAULT_DEVICE_USER = "Internal/devicemgt-user";
public static final String DEFAULT_DEVICE_ADMIN = "Internal/devicemgt-admin"; public static final String DEFAULT_DEVICE_ADMIN = "Internal/devicemgt-admin";
public static final String CLAIM_EMAIL_ADDRESS = "http://wso2.org/claims/emailaddress";
public static final String CLAIM_FIRST_NAME = "http://wso2.org/claims/givenname";
// Permissions that are given for a normal device user. // Permissions that are given for a normal device user.
public static final Permission[] PERMISSIONS_FOR_DEVICE_USER = { public static final Permission[] PERMISSIONS_FOR_DEVICE_USER = {
new Permission("/permission/admin/Login", "ui.execute"), new Permission("/permission/admin/Login", "ui.execute"),

@ -35,6 +35,7 @@ import org.wso2.carbon.device.mgt.common.permission.mgt.PermissionManagerService
import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService; import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService;
import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService;
import org.wso2.carbon.device.mgt.common.spi.DeviceTypeGeneratorService; import org.wso2.carbon.device.mgt.common.spi.DeviceTypeGeneratorService;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService; import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService;
import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagerProviderServiceImpl; import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagerProviderServiceImpl;
@ -56,6 +57,8 @@ import org.wso2.carbon.device.mgt.core.notification.mgt.NotificationManagementSe
import org.wso2.carbon.device.mgt.core.notification.mgt.dao.NotificationManagementDAOFactory; import org.wso2.carbon.device.mgt.core.notification.mgt.dao.NotificationManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.operation.mgt.OperationManagerImpl; import org.wso2.carbon.device.mgt.core.operation.mgt.OperationManagerImpl;
import org.wso2.carbon.device.mgt.core.operation.mgt.dao.OperationManagementDAOFactory; import org.wso2.carbon.device.mgt.core.operation.mgt.dao.OperationManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.OTPManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.otp.mgt.service.OTPManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.permission.mgt.PermissionManagerServiceImpl; import org.wso2.carbon.device.mgt.core.permission.mgt.PermissionManagerServiceImpl;
import org.wso2.carbon.device.mgt.core.privacy.PrivacyComplianceProvider; import org.wso2.carbon.device.mgt.core.privacy.PrivacyComplianceProvider;
import org.wso2.carbon.device.mgt.core.privacy.impl.PrivacyComplianceProviderImpl; import org.wso2.carbon.device.mgt.core.privacy.impl.PrivacyComplianceProviderImpl;
@ -178,6 +181,7 @@ public class DeviceManagementServiceComponent {
NotificationManagementDAOFactory.init(dsConfig); NotificationManagementDAOFactory.init(dsConfig);
OperationManagementDAOFactory.init(dsConfig); OperationManagementDAOFactory.init(dsConfig);
MetadataManagementDAOFactory.init(dsConfig); MetadataManagementDAOFactory.init(dsConfig);
OTPManagementDAOFactory.init(dsConfig.getJndiLookupDefinition().getJndiName());
/*Initialize the device cache*/ /*Initialize the device cache*/
DeviceManagerUtil.initializeDeviceCache(); DeviceManagerUtil.initializeDeviceCache();
@ -330,7 +334,10 @@ public class DeviceManagementServiceComponent {
MetadataManagementService metadataManagementService = new MetadataManagementServiceImpl(); MetadataManagementService metadataManagementService = new MetadataManagementServiceImpl();
bundleContext.registerService(MetadataManagementService.class.getName(), metadataManagementService, null); bundleContext.registerService(MetadataManagementService.class.getName(), metadataManagementService, null);
/* Registering App Management service */ OTPManagementService otpManagementService = new OTPManagementServiceImpl();
bundleContext.registerService(OTPManagementService.class.getName(), otpManagementService, null);
/* Registering App Management service */
try { try {
AppManagementConfigurationManager.getInstance().initConfig(); AppManagementConfigurationManager.getInstance().initConfig();
AppManagementConfig appConfig = AppManagementConfig appConfig =

@ -0,0 +1,33 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao;
import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException;
import org.wso2.carbon.device.mgt.core.otp.mgt.util.ConnectionManagerUtil;
import java.sql.Connection;
/**
* This class deals with getting the DB connection.
*/
public abstract class AbstractDAOImpl {
protected Connection getDBConnection() throws DBConnectionException {
return ConnectionManagerUtil.getDBConnection();
}
}

@ -0,0 +1,64 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao;
import org.wso2.carbon.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import org.wso2.carbon.device.mgt.core.otp.mgt.exception.OTPManagementDAOException;
public interface OTPManagementDAO {
/**
* Save OTP token data and tenant details of registering user
* @param oneTimePinDTO OTPMailDTO
* @return Primary key of the newly adding data raw
* @throws OTPManagementDAOException if error occurred whule storing data
*/
int addOTPData(OneTimePinDTO oneTimePinDTO) throws OTPManagementDAOException;
/**
* Get OTP data for requesting One Time Token
* @param oneTimeToken One Time Token
* @return {@link OneTimePinDTO}
* @throws OTPManagementDAOException if error ocured while getting OTP data for requesting one time token
*/
OneTimePinDTO getOTPDataByToken (String oneTimeToken) throws OTPManagementDAOException;
/**
* Expire the OTP
* @param oneTimeToken OTP
* @throws OTPManagementDAOException if error occurred while updating the OTP validity.
*/
boolean expireOneTimeToken(String oneTimeToken) throws OTPManagementDAOException;
/**
* Update OTP with renewed OTP
* @param id ID
* @param oneTimeToken One Time Token
* @throws OTPManagementDAOException if error occured while updating OTP
*/
void renewOneTimeToken(int id, String oneTimeToken) throws OTPManagementDAOException;
/**
* To veify whether email and email type exists or not
* @param email email
* @param emailType email type
* @return true if email and email type exists otherwise returns false
* @throws OTPManagementDAOException if error occurred while verify existance of the email and email type
*/
boolean isEmailExist (String email, String emailType) throws OTPManagementDAOException;
}

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl.GenericOTPManagementDAOImpl;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl.OracleOTPManagementDAOImpl;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl.PostgreSQLOTPManagementDAOImpl;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl.SQLServerOTPManagementDAOImpl;
import org.wso2.carbon.device.mgt.core.otp.mgt.util.ConnectionManagerUtil;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* This class intends to act as the primary entity that hides all DAO instantiation related complexities and logic so
* that the business objection handling layer doesn't need to be aware of the same providing seamless plug-ability of
* different data sources, connection acquisition mechanisms as well as different forms of DAO implementations to the
* high-level implementations that require Application management related metadata persistence.
*/
public class OTPManagementDAOFactory {
private static String databaseEngine;
private static final Log log = LogFactory.getLog(OTPManagementDAOFactory.class);
public static void init(String datasourceName) {
ConnectionManagerUtil.resolveDataSource(datasourceName);
databaseEngine = ConnectionManagerUtil.getDatabaseType();
}
public static void init(DataSource dtSource) {
try (Connection connection = dtSource.getConnection()) {
databaseEngine = connection.getMetaData().getDatabaseProductName();
} catch (SQLException e) {
log.error("Error occurred while retrieving config.datasource connection", e);
}
}
public static OTPManagementDAO getOTPManagementDAO() {
if (databaseEngine != null) {
switch (databaseEngine) {
case DeviceManagementConstants.DataBaseTypes.DB_TYPE_H2:
case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MYSQL:
return new GenericOTPManagementDAOImpl();
case DeviceManagementConstants.DataBaseTypes.DB_TYPE_POSTGRESQL:
return new PostgreSQLOTPManagementDAOImpl();
case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MSSQL:
return new SQLServerOTPManagementDAOImpl();
case DeviceManagementConstants.DataBaseTypes.DB_TYPE_ORACLE:
return new OracleOTPManagementDAOImpl();
default:
throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine);
}
}
throw new IllegalStateException("Database engine has not initialized properly.");
}
}

@ -0,0 +1,243 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException;
import org.wso2.carbon.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.AbstractDAOImpl;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.OTPManagementDAO;
import org.wso2.carbon.device.mgt.core.otp.mgt.exception.OTPManagementDAOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Calendar;
public class GenericOTPManagementDAOImpl extends AbstractDAOImpl implements OTPManagementDAO {
private static final Log log = LogFactory.getLog(GenericOTPManagementDAOImpl.class);
@Override
public int addOTPData(OneTimePinDTO oneTimePinDTO) throws OTPManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to create an OTP data entry");
log.debug("OTP Details : ");
log.debug("OTP key : " + oneTimePinDTO.getOtpToken() + " Email : " + oneTimePinDTO.getEmail());
}
String sql = "INSERT INTO DM_OTP_DATA "
+ "(OTP_TOKEN, "
+ "EMAIL, "
+ "EMAIL_TYPE, "
+ "META_INFO, "
+ "CREATED_AT,"
+ "TENANT_ID,"
+ "USERNAME) VALUES (?, ?, ?, ?, ?, ?, ?)";
try {
Connection conn = this.getDBConnection();
Calendar calendar = Calendar.getInstance();
Timestamp timestamp = new Timestamp(calendar.getTime().getTime());
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, oneTimePinDTO.getOtpToken());
stmt.setString(2, oneTimePinDTO.getEmail());
stmt.setString(3, oneTimePinDTO.getEmailType());
stmt.setString(4, oneTimePinDTO.getMetaInfo());
stmt.setTimestamp(5, timestamp);
stmt.setInt(6, oneTimePinDTO.getTenantId());
stmt.setString(7, oneTimePinDTO.getUsername());
stmt.executeUpdate();
try (ResultSet rs = stmt.getGeneratedKeys()) {
if (rs.next()) {
return rs.getInt(1);
}
return -1;
}
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to create an opt entry for email "
+ oneTimePinDTO.getEmail();
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while executing SQL to create an otp entry for email " + oneTimePinDTO.getEmail();
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
}
}
@Override
public OneTimePinDTO getOTPDataByToken (String oneTimeToken) throws OTPManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to get an OTP data entry for OTP");
log.debug("OTP Details : OTP key : " + oneTimeToken );
}
String sql = "SELECT "
+ "ID, "
+ "OTP_TOKEN, "
+ "EMAIL, "
+ "EMAIL_TYPE, "
+ "META_INFO, "
+ "CREATED_AT, "
+ "EXPIRY_TIME, "
+ "IS_EXPIRED, "
+ "TENANT_ID, "
+ "USERNAME FROM DM_OTP_DATA "
+ "WHERE OTP_TOKEN = ?";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, oneTimeToken);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
OneTimePinDTO oneTimePinDTO = new OneTimePinDTO();
oneTimePinDTO.setId(rs.getInt("ID"));
oneTimePinDTO.setOtpToken(rs.getString("OTP_TOKEN"));
oneTimePinDTO.setEmail(rs.getString("EMAIL"));
oneTimePinDTO.setEmailType(rs.getString("EMAIL_TYPE"));
oneTimePinDTO.setMetaInfo(rs.getString("META_INFO"));
oneTimePinDTO.setCreatedAt(rs.getTimestamp("CREATED_AT"));
oneTimePinDTO.setExpiryTime(rs.getInt("EXPIRY_TIME"));
oneTimePinDTO.setExpired(rs.getBoolean("IS_EXPIRED"));
oneTimePinDTO.setTenantId(rs.getInt("TENANT_ID"));
oneTimePinDTO.setUsername(rs.getString("USERNAME"));
return oneTimePinDTO;
}
return null;
}
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get OPT data for given OTP. OTP: "
+ oneTimeToken;
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while executing SQL to get OTP data for OTP. One time token: " + oneTimeToken;
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
}
}
@Override
public boolean expireOneTimeToken(String oneTimeToken) throws OTPManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to update an OTP data entry for OTP");
log.debug("OTP Details : OTP key : " + oneTimeToken );
}
String sql = "UPDATE DM_OTP_DATA "
+ "SET "
+ "IS_EXPIRED = ? "
+ "WHERE OTP_TOKEN = ?";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setBoolean(1, true);
stmt.setString(2, oneTimeToken);
return stmt.executeUpdate() == 1;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to update the OTP token validity.";
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when obtaining database connection for updating the OTP token validity.";
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
}
}
@Override
public void renewOneTimeToken(int id, String oneTimeToken) throws OTPManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to update an OTP data entry for OTP");
log.debug("OTP Details : OTP key : " + oneTimeToken );
}
String sql = "UPDATE DM_OTP_DATA "
+ "SET "
+ "OTP_TOKEN = ?, "
+ "CREATED_AT = ? "
+ "WHERE ID = ?";
try {
Connection conn = this.getDBConnection();
Calendar calendar = Calendar.getInstance();
Timestamp timestamp = new Timestamp(calendar.getTime().getTime());
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, oneTimeToken);
stmt.setTimestamp(2, timestamp);
stmt.setInt(3, id);
stmt.executeUpdate();
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to update the OTP token.";
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when executing sql query to update the OTP token.";
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
}
}
@Override
public boolean isEmailExist (String email, String emailType) throws OTPManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to verify whether email was registed with emai type in OTP");
log.debug("OTP Details : email : " + email + " email type: " + emailType );
}
String sql = "SELECT "
+ "ID "
+ "FROM DM_OTP_DATA "
+ "WHERE EMAIL = ? AND "
+ "EMAIL_TYPE = ?";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, email);
stmt.setString(2, emailType);
try (ResultSet rs = stmt.executeQuery()) {
return rs.next();
}
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to verify email and email type exist in OTP."
+ " Email: " + email + "Email Type: " + emailType;
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while executing SQL to verify email and email type exist in OTP. Email: "
+ email + "Email Type: " + emailType;
log.error(msg, e);
throw new OTPManagementDAOException(msg, e);
}
}
}

@ -0,0 +1,24 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl;
/**
* This handles OTP managing DAO methods which are specific to Oracle.
*/
public class OracleOTPManagementDAOImpl extends GenericOTPManagementDAOImpl{
}

@ -0,0 +1,25 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl;
/**
* This handles OTP managing DAO methods which are specific to PostgreSQL.
*/
public class PostgreSQLOTPManagementDAOImpl extends GenericOTPManagementDAOImpl{
}

@ -0,0 +1,24 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.dao.impl;
/**
* This handles OTP managing DAO methods which are specific to MSSQL.
*/
public class SQLServerOTPManagementDAOImpl extends GenericOTPManagementDAOImpl{
}

@ -0,0 +1,31 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.exception;
/**
* Exception thrown during the ApplicationDTO Management DAO operations.
*/
public class OTPManagementDAOException extends Exception {
public OTPManagementDAOException(String message, Throwable throwable) {
super(message, throwable);
}
public OTPManagementDAOException(String message) {
super(message, new Exception());
}
}

@ -0,0 +1,461 @@
/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.service;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException;
import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.OTPManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.UnAuthorizedException;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitation;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentType;
import org.wso2.carbon.device.mgt.common.metadata.mgt.Metadata;
import org.wso2.carbon.device.mgt.common.otp.mgt.OTPEmailTypes;
import org.wso2.carbon.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.device.mgt.core.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager;
import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig;
import org.wso2.carbon.device.mgt.core.config.keymanager.KeyManagerConfigurations;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.OTPManagementDAO;
import org.wso2.carbon.device.mgt.common.otp.mgt.wrapper.OTPWrapper;
import org.wso2.carbon.device.mgt.core.otp.mgt.dao.OTPManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.otp.mgt.exception.OTPManagementDAOException;
import org.wso2.carbon.device.mgt.core.otp.mgt.util.ConnectionManagerUtil;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.service.EmailMetaInfo;
import org.apache.commons.validator.routines.EmailValidator;
import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
import org.wso2.carbon.user.api.Tenant;
import org.wso2.carbon.user.api.UserStoreException;
import static org.wso2.carbon.device.mgt.common.DeviceManagementConstants.OTPProperties;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
public class OTPManagementServiceImpl implements OTPManagementService {
private static final Log log = LogFactory.getLog(OTPManagementServiceImpl.class);
private OTPManagementDAO otpManagementDAO;
public OTPManagementServiceImpl() {
initDataAccessObjects();
}
private void initDataAccessObjects() {
otpManagementDAO = OTPManagementDAOFactory.getOTPManagementDAO();
}
@Override
public void sendUserVerifyingMail(OTPWrapper otpWrapper) throws OTPManagementException, DeviceManagementException {
Tenant tenant = validateTenantCreatingDetails(otpWrapper);
OneTimePinDTO oneTimePinDTO = createOneTimePin(otpWrapper.getEmail(), otpWrapper.getEmailType(),
otpWrapper.getUsername(), tenant, -1234);
try {
ConnectionManagerUtil.beginDBTransaction();
if (this.otpManagementDAO.addOTPData(oneTimePinDTO) == -1) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "OTP data saving failed. Please, contact Administrator";
log.error(msg);
throw new OTPManagementException(msg);
}
Properties props = new Properties();
props.setProperty("first-name", tenant.getAdminFirstName());
props.setProperty("otp-token", oneTimePinDTO.getOtpToken());
sendMail(props, tenant.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE);
ConnectionManagerUtil.commitDBTransaction();
} catch (TransactionManagementException e) {
String msg = "Error occurred while disabling AutoCommit.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (OTPManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while saving the OTP data. Email address: " + oneTimePinDTO.getEmail();
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public OneTimePinDTO isValidOTP(String oneTimeToken) throws OTPManagementException, BadRequestException {
if (StringUtils.isBlank(oneTimeToken)){
String msg = "Received blank OTP to verify. OTP: " + oneTimeToken;
log.error(msg);
throw new BadRequestException(msg);
}
OneTimePinDTO oneTimePinDTO = getOTPDataByToken(oneTimeToken);
if (oneTimePinDTO == null) {
String msg = "Couldn't found OTP data for the requesting OTP " + oneTimeToken + " In the system.";
log.error(msg);
throw new BadRequestException(msg);
}
if (oneTimePinDTO.isExpired()) {
log.warn("Token is expired. OTP: " + oneTimeToken);
return null;
}
Calendar calendar = Calendar.getInstance();
Timestamp currentTimestamp = new Timestamp(calendar.getTime().getTime());
Timestamp expiredTimestamp = new Timestamp(
oneTimePinDTO.getCreatedAt().getTime() + oneTimePinDTO.getExpiryTime() * 1000);
if (currentTimestamp.after(expiredTimestamp)) {
String renewedOTP = UUID.randomUUID().toString();
renewOTP(oneTimePinDTO, renewedOTP);
Gson gson = new Gson();
Tenant tenant = gson.fromJson(oneTimePinDTO.getMetaInfo(), Tenant.class);
Properties props = new Properties();
props.setProperty("first-name", tenant.getAdminFirstName());
props.setProperty("otp-token", renewedOTP);
sendMail(props, oneTimePinDTO.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE);
return null;
}
return oneTimePinDTO;
}
@Override
public void completeSelfRegistration(String oneTimeToken, String email, Map<String, String> properties)
throws OTPManagementException {
try {
invalidateOTP(oneTimeToken);
Properties props = new Properties();
properties.forEach(props::setProperty);
sendMail(props, email, DeviceManagementConstants.EmailAttributes.USER_WELCOME_TEMPLATE);
} catch (OTPManagementException e) {
String msg = "Error occurred while completing the self registration via OTP";
log.error(msg, e);
throw new OTPManagementException(msg, e);
}
}
/**
* Invalidate the OTP
* @param oneTimeToken OTP
* @throws OTPManagementException If error occurred while invalidating the OTP
*/
private void invalidateOTP(String oneTimeToken) throws OTPManagementException {
try {
ConnectionManagerUtil.beginDBTransaction();
if (!otpManagementDAO.expireOneTimeToken(oneTimeToken)) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Couldn't find OTP entry for OTP: " + oneTimeToken;
log.error(msg);
throw new OTPManagementException(msg);
}
ConnectionManagerUtil.commitDBTransaction();
} catch (OTPManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while invalidate the OTP: " + oneTimeToken;
log.error(msg);
throw new OTPManagementException(msg);
} catch (TransactionManagementException e) {
String msg = "Error occurred while disabling AutoCommit to invalidate OTP.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection to invalidate OPT.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public void sendDeviceEnrollmentInvitationMail(DeviceEnrollmentInvitation deviceEnrollmentInvitation)
throws OTPManagementException {
DeviceManagementProviderService dms = DeviceManagementDataHolder.getInstance().getDeviceManagementProvider();
StringBuilder enrollmentSteps = new StringBuilder();
DeviceEnrollmentInvitationDetails deviceEnrollmentInvitationDetails;
for (DeviceEnrollmentType deviceEnrollmentType : deviceEnrollmentInvitation.getDeviceEnrollmentTypes()) {
deviceEnrollmentInvitationDetails = dms.getDeviceEnrollmentInvitationDetails(
deviceEnrollmentType.getDeviceType());
if (deviceEnrollmentInvitationDetails != null &&
deviceEnrollmentInvitationDetails.getEnrollmentDetails() != null) {
for (String enrollmentType : deviceEnrollmentType.getEnrollmentType()) {
deviceEnrollmentInvitationDetails.getEnrollmentDetails().stream()
.filter(details -> enrollmentType.equals(details.getEnrollmentType())).findFirst()
.ifPresent(details -> enrollmentSteps.append(details.getEnrollmentSteps()));
}
}
}
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
OneTimePinDTO oneTimePinDTO;
Properties props = new Properties();
props.setProperty("enrollment-steps", enrollmentSteps.toString());
try {
for (String username : deviceEnrollmentInvitation.getUsernames()) {
String emailAddress = DeviceManagerUtil.getUserClaimValue(
username, DeviceManagementConstants.User.CLAIM_EMAIL_ADDRESS);
oneTimePinDTO = createOneTimePin(emailAddress, OTPEmailTypes.DEVICE_ENROLLMENT.toString(), username,
null, tenantId);
props.setProperty("first-name", DeviceManagerUtil.
getUserClaimValue(username, DeviceManagementConstants.User.CLAIM_FIRST_NAME));
props.setProperty("username", username);
props.setProperty("otp-token", oneTimePinDTO.getOtpToken());
sendMail(props, emailAddress, DeviceManagementConstants.EmailAttributes.USER_ENROLLMENT_TEMPLATE);
}
} catch (UserStoreException e) {
String msg = "Error occurred while getting claim values to invite user";
log.error(msg, e);
throw new OTPManagementException(msg, e);
}
}
/**
* Create One Time Token
* @param email email
* @param emailType email type
* @param userName username
* @param metaDataObj meta data object
* @param tenantId tenant Id
* @return {@link OneTimePinDTO}
*/
private OneTimePinDTO createOneTimePin(String email, String emailType, String userName, Object metaDataObj,
int tenantId) {
String otpValue = UUID.randomUUID().toString();
Gson gson = new Gson();
String metaInfo = gson.toJson(metaDataObj);
OneTimePinDTO oneTimePinDTO = new OneTimePinDTO();
oneTimePinDTO.setEmail(email);
oneTimePinDTO.setTenantId(tenantId);
oneTimePinDTO.setUsername(userName);
oneTimePinDTO.setEmailType(emailType);
oneTimePinDTO.setMetaInfo(metaInfo);
oneTimePinDTO.setOtpToken(otpValue);
return oneTimePinDTO;
}
/**
* Get OTPData from DB
* @param oneTimeToken One Time Token
* @return {@link OneTimePinDTO}
* @throws OTPManagementException if error occurred while getting OTP data for given OTP in DB
*/
private OneTimePinDTO getOTPDataByToken ( String oneTimeToken) throws OTPManagementException {
try {
ConnectionManagerUtil.openDBConnection();
return otpManagementDAO.getOTPDataByToken(oneTimeToken);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection to validate the given OTP.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (OTPManagementDAOException e) {
String msg = "Error occurred while getting OTP data from DB. OTP: " + oneTimeToken;
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
/**
* Validate Tenant details
* @param otpWrapper OTP-Wrapper
* @return {@link Tenant} if its valid payload otherwise throws {@link DeviceManagementException}
* @throws DeviceManagementException if invalid payload or unauthorized request received
*/
private Tenant validateTenantCreatingDetails(OTPWrapper otpWrapper) throws DeviceManagementException {
DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance()
.getDeviceManagementConfig();
KeyManagerConfigurations kmConfig = deviceManagementConfig.getKeyManagerConfigurations();
if (StringUtils.isBlank(otpWrapper.getUsername())) {
String msg = "Received Blank username to create OTP. Username: " + otpWrapper.getUsername();
log.error(msg);
throw new BadRequestException(msg);
}
String[] superTenantDetails = otpWrapper.getUsername().split("@");
if (!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(superTenantDetails[superTenantDetails.length - 1])
|| !superTenantDetails[0].equals(kmConfig.getAdminUsername())) {
String msg = "You don't have required permission to create OTP";
log.error(msg);
throw new UnAuthorizedException(msg);
}
Tenant tenant = new Tenant();
List<Metadata> properties = otpWrapper.getProperties();
for (Metadata property : properties) {
if (property == null) {
String msg = "Received invalid property to create OTP.";
log.error(msg);
throw new BadRequestException(msg);
}
switch (property.getMetaKey()) {
case OTPProperties.FIRST_NAME:
String firstName = property.getMetaValue();
if (StringUtils.isBlank(firstName)) {
String msg = "Received empty or blank first name field with OTP creating payload.";
log.error(msg);
throw new BadRequestException(msg);
}
tenant.setAdminFirstName(firstName);
break;
case OTPProperties.LAST_NAME:
String lastName = property.getMetaValue();
if (StringUtils.isBlank(lastName)) {
String msg = "Received empty or blank last name field with OTP creating payload.";
log.error(msg);
throw new BadRequestException(msg);
}
tenant.setAdminLastName(lastName);
break;
case OTPProperties.TENANT_ADMIN_PASSWORD:
String pwd = property.getMetaValue();
if (StringUtils.isBlank(pwd)) {
String msg = "Received empty or blank admin password field with OTP creating payload.";
log.error(msg);
throw new BadRequestException(msg);
}
tenant.setAdminPassword(pwd);
break;
default:
String msg = "Received invalid key with OTP properties for creating OTP.";
log.error(msg);
throw new BadRequestException(msg);
}
}
if (StringUtils.isBlank(otpWrapper.getEmail())) {
String msg = "Received empty or blank email field with OTP creating payload.";
log.error(msg);
throw new BadRequestException(msg);
}
EmailValidator validator = EmailValidator.getInstance();
if (!validator.isValid(otpWrapper.getEmail())) {
String msg = "Found invalid email. Hence please verify the email address and re-try. Email: " + otpWrapper
.getEmail();
log.error(msg);
throw new BadRequestException(msg);
}
if (StringUtils.isBlank(otpWrapper.getEmailType())) {
String msg = "Received empty or blank email type field with OTP creating payload.";
log.error(msg);
throw new BadRequestException(msg);
}
try {
ConnectionManagerUtil.openDBConnection();
if (otpManagementDAO.isEmailExist(otpWrapper.getEmail(), otpWrapper.getEmailType())) {
String msg = "Email is registered to execute the same action. Hence can't proceed.";
log.error(msg);
throw new BadRequestException(msg);
}
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection to validate the given email and email type.";
log.error(msg);
throw new DeviceManagementException(msg);
} catch (OTPManagementDAOException e) {
String msg = "Error occurred while executing SQL query to validate the given email and email type.";
log.error(msg);
throw new DeviceManagementException(msg);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
String[] tenantUsernameDetails = otpWrapper.getEmail().split("@");
tenant.setAdminName(tenantUsernameDetails[0]);
tenant.setDomain(tenantUsernameDetails[tenantUsernameDetails.length - 1]);
tenant.setEmail(otpWrapper.getEmail());
return tenant;
}
/**
* If OTP expired, resend the user verifying mail with renewed OTP
* @param props Mail body properties
* @param mailAddress Mail Address of the User
* @param template Mail template to be used
* @throws OTPManagementException if error occurred while resend the user verifying mail
*/
private void sendMail(Properties props, String mailAddress, String template) throws OTPManagementException {
try {
EmailMetaInfo metaInfo = new EmailMetaInfo(mailAddress, props);
DeviceManagementDataHolder.getInstance().getDeviceManagementProvider()
.sendEnrolmentInvitation(template, metaInfo);
} catch (DeviceManagementException e) {
String msg = "Error occurred while sending email using email template '" + template + "'.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (ConfigurationManagementException e) {
String msg = "Configuration error occurred. Hence mail sending failed.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
}
}
/**
* Renew the OTP
* @param oneTimePinDTO {@link OneTimePinDTO}
* @param renewedOTP Renewed OTP
* @throws OTPManagementException if error occurred while renew the OTP
*/
private void renewOTP(OneTimePinDTO oneTimePinDTO, String renewedOTP) throws OTPManagementException {
try {
ConnectionManagerUtil.beginDBTransaction();
this.otpManagementDAO.renewOneTimeToken(oneTimePinDTO.getId(), renewedOTP);
ConnectionManagerUtil.commitDBTransaction();
} catch (TransactionManagementException e) {
String msg = "Error occurred while disabling AutoCommit to renew the OTP.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting database connection to renew the OTP.";
log.error(msg, e);
throw new OTPManagementException(msg, e);
} catch (OTPManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while renew the OTP. OTP: " + renewedOTP;
log.error(msg, e);
throw new OTPManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
}

@ -0,0 +1,211 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.otp.mgt.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* ConnectionManagerUtil is responsible for handling all the datasource connections utilities.
*/
public class ConnectionManagerUtil {
private static final Log log = LogFactory.getLog(ConnectionManagerUtil.class);
private static final ThreadLocal<Connection> currentConnection = new ThreadLocal<>();
private static DataSource dataSource;
public static void openDBConnection() throws DBConnectionException {
Connection conn = currentConnection.get();
if (conn != null) {
String msg = "Database connection has already been obtained.";
log.error(msg);
throw new IllegalTransactionStateException(msg);
}
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
String msg = "Failed to get a database connection.";
log.error(msg, e);
throw new DBConnectionException(msg, e);
}
currentConnection.set(conn);
}
public static Connection getDBConnection() throws DBConnectionException {
Connection conn = currentConnection.get();
if (conn == null) {
try {
conn = dataSource.getConnection();
currentConnection.set(conn);
} catch (SQLException e) {
throw new DBConnectionException("Failed to get database connection.", e);
}
}
return conn;
}
public static void beginDBTransaction() throws TransactionManagementException, DBConnectionException {
Connection conn = currentConnection.get();
if (conn == null) {
conn = getDBConnection();
} else if (inTransaction(conn)) {
String msg = "Transaction has already been started.";
log.error(msg);
throw new IllegalTransactionStateException(msg);
}
try {
conn.setAutoCommit(false);
} catch (SQLException e) {
String msg = "Error occurred while starting a database transaction.";
log.error(msg, e);
throw new TransactionManagementException(msg, e);
}
}
public static void endDBTransaction() throws TransactionManagementException {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("Database connection is not active.");
}
if (!inTransaction(conn)) {
throw new IllegalTransactionStateException("Transaction has not been started.");
}
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
throw new TransactionManagementException("Error occurred while ending database transaction.", e);
}
}
public static void commitDBTransaction() {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("Database connection is not active.");
}
if (!inTransaction(conn)) {
throw new IllegalTransactionStateException("Transaction has not been started.");
}
try {
conn.commit();
} catch (SQLException e) {
log.error("Error occurred while committing the transaction", e);
}
}
public static void rollbackDBTransaction() {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("Database connection is not active.");
}
if (!inTransaction(conn)) {
throw new IllegalTransactionStateException("Transaction has not been started.");
}
try {
conn.rollback();
} catch (SQLException e) {
log.warn("Error occurred while roll-backing the transaction", e);
}
}
public static void closeDBConnection() {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("Database connection is not active.");
}
try {
conn.close();
} catch (SQLException e) {
log.error("Error occurred while closing the connection", e);
}
currentConnection.remove();
}
private static boolean inTransaction(Connection conn) {
boolean inTransaction = true;
try {
if (conn.getAutoCommit()) {
inTransaction = false;
}
} catch (SQLException e) {
throw new IllegalTransactionStateException("Failed to get transaction state.");
}
return inTransaction;
}
public static boolean isTransactionStarted() throws DBConnectionException {
Connection connection = getDBConnection();
return inTransaction(connection);
}
/**
* Resolve the datasource from the datasource definition.
*
* @param dataSourceName Name of the datasource
* @return DataSource resolved by the datasource name
*/
public static DataSource resolveDataSource(String dataSourceName) {
try {
dataSource = InitialContext.doLookup(dataSourceName);
} catch (Exception e) {
throw new RuntimeException("Error in looking up data source: " + e.getMessage(), e);
}
return dataSource;
}
public static String getDatabaseType() {
try (Connection connection = dataSource.getConnection()) {
return connection.getMetaData().getDatabaseProductName();
} catch (SQLException e) {
log.error("Error occurred while retrieving config.datasource connection", e);
}
return null;
}
/**
* To check whether particular database that is used for application management supports batch query execution.
*
* @return true if batch query is supported, otherwise false.
*/
public static boolean isBatchQuerySupported() {
try (Connection connection = dataSource.getConnection()) {
return connection.getMetaData().supportsBatchUpdates();
} catch (SQLException e) {
log.error("Error occurred while checking whether database supports batch updates", e);
}
return false;
}
public static void init(DataSource dtSource) {
dataSource = dtSource;
}
}

@ -14,8 +14,8 @@
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ *
/* *
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
* *
* Entgra (pvt) Ltd. licenses this file to you under the Apache License, * Entgra (pvt) Ltd. licenses this file to you under the Apache License,
@ -32,23 +32,6 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.device.mgt.core.service; package org.wso2.carbon.device.mgt.core.service;
@ -76,6 +59,7 @@ import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManageme
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceConfiguration; import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceConfiguration;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration; import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.device.details.DeviceData; import org.wso2.carbon.device.mgt.common.device.details.DeviceData;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.license.mgt.License; import org.wso2.carbon.device.mgt.common.license.mgt.License;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
@ -89,7 +73,6 @@ import org.wso2.carbon.device.mgt.core.dto.DeviceType;
import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion;
import org.wso2.carbon.device.mgt.core.geo.GeoCluster; import org.wso2.carbon.device.mgt.core.geo.GeoCluster;
import org.wso2.carbon.device.mgt.core.geo.geoHash.GeoCoordinate; import org.wso2.carbon.device.mgt.core.geo.geoHash.GeoCoordinate;
import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Date; import java.util.Date;
@ -927,4 +910,12 @@ public interface DeviceManagementProviderService {
*/ */
List<Device> getDeviceByIdList(List<String> deviceIdentifiers) List<Device> getDeviceByIdList(List<String> deviceIdentifiers)
throws DeviceManagementException; throws DeviceManagementException;
/**
* Retrieve device enrollment details to be sent device enrollment invitation.
* This has the relevant enrollment steps of each enrollment types.
* @param deviceType Device type of the required device enrollment details
* @return enrollment steps of each enrollment types which are provided in the device type xml file
*/
DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails(String deviceType);
} }

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*
*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ */
package org.wso2.carbon.device.mgt.core.service; package org.wso2.carbon.device.mgt.core.service;
@ -74,6 +91,7 @@ import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.license.mgt.License; import org.wso2.carbon.device.mgt.common.license.mgt.License;
import org.wso2.carbon.device.mgt.common.license.mgt.LicenseManagementException; import org.wso2.carbon.device.mgt.common.license.mgt.LicenseManagementException;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
@ -4187,4 +4205,11 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
DeviceManagementDAOFactory.closeConnection(); DeviceManagementDAOFactory.closeConnection();
} }
} }
@Override
public DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails(String deviceType) {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
DeviceManagementService dms = pluginRepository.getDeviceManagementService(deviceType, tenantId);
return dms.getDeviceEnrollmentInvitationDetails();
}
} }

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*
*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ */
package org.wso2.carbon.device.mgt.core.util; package org.wso2.carbon.device.mgt.core.util;
@ -79,6 +96,7 @@ import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerSer
import org.wso2.carbon.user.api.TenantManager; import org.wso2.carbon.user.api.TenantManager;
import org.wso2.carbon.user.api.UserRealm; import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.ConfigurationContextService; import org.wso2.carbon.utils.ConfigurationContextService;
import org.wso2.carbon.utils.NetworkUtils; import org.wso2.carbon.utils.NetworkUtils;
@ -1047,4 +1065,17 @@ public final class DeviceManagerUtil {
} }
return roleList; return roleList;
} }
/**
* Retrieve the value of the user property from the user profile
* @param username of the user
* @param claimUri name of the claim
* @return value for the claim uri of user
* @throws UserStoreException when there is error in retrieving the user store manager
*/
public static String getUserClaimValue(String username, String claimUri) throws UserStoreException {
UserStoreManager userStoreManager = CarbonContext.getThreadLocalCarbonContext().getUserRealm()
.getUserStoreManager();
return userStoreManager.getUserClaimValue(username, claimUri, null);
}
} }

@ -37,6 +37,7 @@ import org.wso2.carbon.device.mgt.common.*;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager; import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.general.GeneralConfig; import org.wso2.carbon.device.mgt.common.general.GeneralConfig;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager;
import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber;
import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig; import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig;
@ -142,4 +143,9 @@ public class TestDeviceManagementService implements DeviceManagementService {
public DeviceTypePlatformDetails getDeviceTypePlatformDetails() { public DeviceTypePlatformDetails getDeviceTypePlatformDetails() {
return null; return null;
} }
@Override
public DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails() {
return null;
}
} }

@ -48,6 +48,7 @@ import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration; import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.general.GeneralConfig; import org.wso2.carbon.device.mgt.common.general.GeneralConfig;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager;
import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber;
import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig; import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig;
@ -92,6 +93,7 @@ public class DeviceTypeManagerService implements DeviceManagementService {
private PullNotificationSubscriber pullNotificationSubscriber; private PullNotificationSubscriber pullNotificationSubscriber;
private final DeviceStatusTaskPluginConfig deviceStatusTaskPluginConfig; private final DeviceStatusTaskPluginConfig deviceStatusTaskPluginConfig;
private DeviceTypePlatformDetails deviceTypePlatformDetails; private DeviceTypePlatformDetails deviceTypePlatformDetails;
private DeviceEnrollmentInvitationDetails deviceEnrollmentInvitationDetails;
private GeneralConfig generalConfig; private GeneralConfig generalConfig;
private boolean isRegistryBasedConfigs = false; private boolean isRegistryBasedConfigs = false;
private boolean isScheduled = false; private boolean isScheduled = false;
@ -116,6 +118,8 @@ public class DeviceTypeManagerService implements DeviceManagementService {
this.setPolicyMonitoringManager(deviceTypeConfiguration.getPolicyMonitoring()); this.setPolicyMonitoringManager(deviceTypeConfiguration.getPolicyMonitoring());
this.setPullNotificationSubscriber(deviceTypeConfiguration.getPullNotificationSubscriberConfig()); this.setPullNotificationSubscriber(deviceTypeConfiguration.getPullNotificationSubscriberConfig());
this.setGeneralConfig(deviceTypeConfiguration); this.setGeneralConfig(deviceTypeConfiguration);
this.deviceEnrollmentInvitationDetails = new DeviceEnrollmentInvitationDetails();
this.setDeviceEnrollmentInvitationDetails(deviceTypeConfiguration);
} }
@Override @Override
@ -259,6 +263,11 @@ public class DeviceTypeManagerService implements DeviceManagementService {
return generalConfig; return generalConfig;
} }
@Override
public DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails() {
return deviceEnrollmentInvitationDetails;
}
private void setProvisioningConfig(String tenantDomain, DeviceTypeConfiguration deviceTypeConfiguration) { private void setProvisioningConfig(String tenantDomain, DeviceTypeConfiguration deviceTypeConfiguration) {
if (deviceTypeConfiguration.getProvisioningConfig() != null) { if (deviceTypeConfiguration.getProvisioningConfig() != null) {
boolean sharedWithAllTenants = deviceTypeConfiguration.getProvisioningConfig().isSharedWithAllTenants(); boolean sharedWithAllTenants = deviceTypeConfiguration.getProvisioningConfig().isSharedWithAllTenants();
@ -354,4 +363,13 @@ public class DeviceTypeManagerService implements DeviceManagementService {
deviceTypePlatformDetails.setDeviceTypePlatformVersion(deviceTypeVersions.getDeviceTypePlatformVersion()); deviceTypePlatformDetails.setDeviceTypePlatformVersion(deviceTypeVersions.getDeviceTypePlatformVersion());
} }
} }
public void setDeviceEnrollmentInvitationDetails(DeviceTypeConfiguration deviceTypeConfiguration) {
DeviceEnrollmentInvitationDetails deviceEnrollmentInvitationDetailsFromConfig = deviceTypeConfiguration
.getDeviceEnrollmentInvitationDetails();
if (deviceEnrollmentInvitationDetailsFromConfig != null) {
deviceEnrollmentInvitationDetails.setEnrollmentDetails(
deviceEnrollmentInvitationDetailsFromConfig.getEnrollmentDetails());
}
}
} }

@ -34,6 +34,7 @@
*/ */
package org.wso2.carbon.device.mgt.extensions.device.type.template.config; package org.wso2.carbon.device.mgt.extensions.device.type.template.config;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformDetails; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformDetails;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
@ -107,6 +108,8 @@ public class DeviceTypeConfiguration {
protected List<String> operations; protected List<String> operations;
@XmlElement(name = "DeviceTypePlatformDetails", required = true) @XmlElement(name = "DeviceTypePlatformDetails", required = true)
protected DeviceTypePlatformDetails deviceTypePlatformDetails; protected DeviceTypePlatformDetails deviceTypePlatformDetails;
@XmlElement(name = "DeviceEnrollmentInvitationDetails", required = true)
protected DeviceEnrollmentInvitationDetails deviceEnrollmentInvitationDetails;
public DeviceTypePlatformDetails getDeviceTypePlatformDetails() { public DeviceTypePlatformDetails getDeviceTypePlatformDetails() {
return deviceTypePlatformDetails; return deviceTypePlatformDetails;
@ -414,4 +417,21 @@ public class DeviceTypeConfiguration {
public void setStartupOperations(List<String> startupOperations) { public void setStartupOperations(List<String> startupOperations) {
this.startupOperations = startupOperations; this.startupOperations = startupOperations;
} }
/**
* Gets the value of device enrollment invitation details which has enrollment steps of enrollment types
* @return device enrollment invitation details
*/
public DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails() {
return deviceEnrollmentInvitationDetails;
}
/**
* Sets the value of device enrollment invitation details from the relevant device type xml file
* @param deviceEnrollmentInvitationDetails {@link DeviceEnrollmentInvitationDetails} object
*/
public void setDeviceEnrollmentInvitationDetails(
DeviceEnrollmentInvitationDetails deviceEnrollmentInvitationDetails) {
this.deviceEnrollmentInvitationDetails = deviceEnrollmentInvitationDetails;
}
} }

@ -44,6 +44,7 @@ import org.wso2.carbon.device.mgt.common.ProvisioningConfig;
import org.wso2.carbon.device.mgt.common.StartupOperationConfig; import org.wso2.carbon.device.mgt.common.StartupOperationConfig;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager; import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager;
import org.wso2.carbon.device.mgt.common.general.GeneralConfig; import org.wso2.carbon.device.mgt.common.general.GeneralConfig;
import org.wso2.carbon.device.mgt.common.invitation.mgt.DeviceEnrollmentInvitationDetails;
import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager;
import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber;
import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig; import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig;
@ -131,4 +132,9 @@ public class TypeXDeviceManagementService implements DeviceManagementService {
public DeviceTypePlatformDetails getDeviceTypePlatformDetails() { public DeviceTypePlatformDetails getDeviceTypePlatformDetails() {
return null; return null;
} }
@Override
public DeviceEnrollmentInvitationDetails getDeviceEnrollmentInvitationDetails() {
return null;
}
} }

@ -0,0 +1,270 @@
/*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.ui.request.interceptor;
import io.entgra.ui.request.interceptor.util.HandlerConstants;
import io.entgra.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang.StringUtils;
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.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.cookie.SM;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.wso2.carbon.device.application.mgt.common.ProxyResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
@WebServlet(
name = "OTPRequestHandlerServlet",
description = "This servlet intercepts the otp-api requests initiated from the user interface and validate "
+ "before forwarding to the backend",
urlPatterns = {
"/otp-invoke/*"
}
)
public class OTPInvokerHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(OTPInvokerHandler.class);
private static final long serialVersionUID = 3109569827313066220L;
private static String apiEndpoint;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
try {
if (validateRequest(req, resp)) {
HttpPost postRequest = new HttpPost(HandlerUtil.generateBackendRequestURL(req, apiEndpoint));
generateRequestEntity(req, postRequest);
ProxyResponse proxyResponse = HandlerUtil.execute(postRequest);
if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) {
log.error("Error occurred while invoking the POST API endpoint.");
HandlerUtil.handleError(resp, proxyResponse);
return;
}
HandlerUtil.handleSuccess(resp, proxyResponse);
}
} catch (FileUploadException e) {
log.error("Error occurred when processing Multipart POST request.", e);
} catch (IOException e) {
log.error("Error occurred when processing POST request.", e);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
if (validateRequest(req, resp)) {
HttpGet getRequest = new HttpGet(HandlerUtil.generateBackendRequestURL(req, apiEndpoint));
copyRequestHeaders(req, getRequest, false);
ProxyResponse proxyResponse = HandlerUtil.execute(getRequest);
if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) {
log.error("Error occurred while invoking the GET API endpoint.");
HandlerUtil.handleError(resp, proxyResponse);
return;
}
HandlerUtil.handleSuccess(resp, proxyResponse);
}
} catch (IOException e) {
log.error("Error occurred when processing GET request.", e);
}
}
@Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp) {
try {
if (validateRequest(req, resp)) {
HttpHead headRequest = new HttpHead(HandlerUtil.generateBackendRequestURL(req, apiEndpoint));
copyRequestHeaders(req, headRequest, false);
ProxyResponse proxyResponse = HandlerUtil.execute(headRequest);
if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) {
log.error("Error occurred while invoking the HEAD API endpoint.");
HandlerUtil.handleError(resp, proxyResponse);
return;
}
HandlerUtil.handleSuccess(resp, proxyResponse);
}
} catch (IOException e) {
log.error("Error occurred when processing HEAD request.", e);
}
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) {
try {
if (validateRequest(req, resp)) {
HttpPut putRequest = new HttpPut(HandlerUtil.generateBackendRequestURL(req, apiEndpoint));
generateRequestEntity(req, putRequest);
ProxyResponse proxyResponse = HandlerUtil.execute(putRequest);
if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) {
log.error("Error occurred while invoking the PUT API endpoint.");
HandlerUtil.handleError(resp, proxyResponse);
return;
}
HandlerUtil.handleSuccess(resp, proxyResponse);
}
} catch (FileUploadException e) {
log.error("Error occurred when processing Multipart PUT request.", e);
} catch (IOException e) {
log.error("Error occurred when processing PUT request.", e);
}
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) {
try {
if (validateRequest(req, resp)) {
HttpDelete deleteRequest = new HttpDelete(HandlerUtil.generateBackendRequestURL(req, apiEndpoint));
copyRequestHeaders(req, deleteRequest, false);
ProxyResponse proxyResponse = HandlerUtil.execute(deleteRequest);
if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) {
log.error("Error occurred while invoking the DELETE API endpoint.");
HandlerUtil.handleError(resp, proxyResponse);
return;
}
HandlerUtil.handleSuccess(resp, proxyResponse);
}
} catch (IOException e) {
log.error("Error occurred when processing DELETE request.", e);
}
}
/**
* Generate te request entity for POST and PUT requests from the incoming request.
*
* @param req incoming {@link HttpServletRequest}.
* @param proxyRequest proxy request instance.
* @throws FileUploadException If unable to parse the incoming request for multipart content extraction.
* @throws IOException If error occurred while generating the request body.
*/
private void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest)
throws FileUploadException, IOException {
if (ServletFileUpload.isMultipartContent(req)) {
ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());
List<FileItem> fileItemList = servletFileUpload.parseRequest(req);
MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
for (FileItem item: fileItemList) {
if (!item.isFormField()) {
entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(),
ContentType.create(item.getContentType()), item.getName()));
} else {
entityBuilder.addTextBody(item.getFieldName(), item.getString());
}
}
proxyRequest.setEntity(entityBuilder.build());
copyRequestHeaders(req, proxyRequest, false);
} else {
if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) ||
StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) {
InputStreamEntity entity = new InputStreamEntity(req.getInputStream(),
Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH)));
proxyRequest.setEntity(entity);
}
copyRequestHeaders(req, proxyRequest, true);
}
}
/**
* Copy incoming request headers to the proxy request.
*
* @param req incoming {@link HttpServletRequest}
* @param httpRequest proxy request instance.
* @param preserveContentType <code>true</code> if content type header needs to be preserved.
* This should be set to <code>false</code> when handling multipart requests as Http
* client will generate the Content-Type header automatically.
*/
private void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) {
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) ||
headerName.equalsIgnoreCase(SM.COOKIE) ||
(!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) {
continue;
}
Enumeration<String> headerValues = req.getHeaders(headerName);
while (headerValues.hasMoreElements()) {
httpRequest.setHeader(headerName, headerValues.nextElement());
}
}
}
/***
* Validates the incoming request.
*
* @param req {@link HttpServletRequest}
* @param resp {@link HttpServletResponse}
* @return If request is a valid one, returns TRUE, otherwise return FALSE
* @throws IOException If and error occurs while witting error response to client side
*/
private static boolean validateRequest(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String schema = req.getScheme();
apiEndpoint = schema + HandlerConstants.SCHEME_SEPARATOR + System.getProperty("iot.core.host")
+ HandlerConstants.COLON + HandlerUtil.getCorePort(schema);
if (StringUtils.isBlank(req.getHeader(HandlerConstants.OTP_HEADER))) {
log.error("Unauthorized, Please provide OTP token.");
handleError(resp, HttpStatus.SC_UNAUTHORIZED);
return false;
}
return true;
}
/**
* Handle error requests
*
* @param resp {@link HttpServletResponse}
* @param errorCode HTTP error status code
* @throws IOException If error occurred when trying to send the error response.
*/
private static void handleError(HttpServletResponse resp, int errorCode)
throws IOException {
ProxyResponse proxyResponse = new ProxyResponse();
proxyResponse.setCode(errorCode);
proxyResponse.setExecutorResponse(
HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode));
HandlerUtil.handleError(resp, proxyResponse);
}
}

@ -47,4 +47,6 @@ public class HandlerConstants {
public static final int INTERNAL_ERROR_CODE = 500; public static final int INTERNAL_ERROR_CODE = 500;
public static final long TIMEOUT = 1200; public static final long TIMEOUT = 1200;
public static final String OTP_HEADER = "one-time-token";
} }

@ -34,6 +34,7 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.wso2.carbon.device.application.mgt.common.ProxyResponse; import org.wso2.carbon.device.application.mgt.common.ProxyResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -203,7 +204,7 @@ public class HandlerUtil {
} }
/** /**
* Get gatway port according to request recieved scheme * Get gateway port according to request received scheme
* @param scheme https or https * @param scheme https or https
* @return {@link String} gateway port * @return {@link String} gateway port
*/ */
@ -215,6 +216,19 @@ public class HandlerUtil {
return gatewayPort; return gatewayPort;
} }
/**
* Get core port according to request received scheme
* @param scheme https or https
* @return {@link String} gateway port
*/
public static String getCorePort(String scheme) {
String productCorePort = System.getProperty("iot.core.https.port");
if (HandlerConstants.HTTP_PROTOCOL.equals(scheme)) {
productCorePort = System.getProperty("iot.core.https.por");
}
return productCorePort;
}
/** /**
* Retrieve Http client based on hostname verification. * Retrieve Http client based on hostname verification.
* @return {@link CloseableHttpClient} http client * @return {@link CloseableHttpClient} http client
@ -242,4 +256,21 @@ public class HandlerUtil {
HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED)); HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED));
handleError(resp, proxyResponse); handleError(resp, proxyResponse);
} }
/**
* Generates the target URL for the proxy request.
*
* @param req incoming {@link HttpServletRequest}
* @param apiEndpoint API Endpoint URL
* @return Target URL
*/
public static String generateBackendRequestURL(HttpServletRequest req, String apiEndpoint) {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(apiEndpoint).append(HandlerConstants.API_COMMON_CONTEXT)
.append(req.getPathInfo().replace(" ", "%20"));
if (StringUtils.isNotEmpty(req.getQueryString())) {
urlBuilder.append("?").append(req.getQueryString());
}
return urlBuilder.toString();
}
} }

@ -93,7 +93,7 @@
org.apache.axis2.transport.http, org.apache.axis2.transport.http,
org.wso2.carbon.certificate.mgt.core.*, org.wso2.carbon.certificate.mgt.core.*,
org.wso2.carbon.device.mgt.core.permission.mgt, org.wso2.carbon.device.mgt.core.permission.mgt,
org.wso2.carbon.device.mgt.common, org.wso2.carbon.device.mgt.common.*,
org.wso2.carbon.device.mgt.common.permission.mgt, org.wso2.carbon.device.mgt.common.permission.mgt,
org.apache.axis2, org.apache.axis2,
org.apache.axis2.client, org.apache.axis2.client,

@ -31,6 +31,7 @@ public final class Constants {
public static final String HEADER_HTTP_ACCEPT = "Accept"; public static final String HEADER_HTTP_ACCEPT = "Accept";
public static final String HEADER_HTTP_AUTHORIZATION = "Authorization"; public static final String HEADER_HTTP_AUTHORIZATION = "Authorization";
public static final String ONE_TIME_TOKEN_HEADER = "one-time-token";
} }
public static final class ContentTypes { public static final class ContentTypes {

@ -0,0 +1,91 @@
/*
* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.webapp.authenticator.framework.authenticator;
import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo;
import org.wso2.carbon.webapp.authenticator.framework.Constants;
import org.wso2.carbon.webapp.authenticator.framework.Utils.Utils;
import org.wso2.carbon.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder;
import java.util.Properties;
public class OneTimeTokenAuthenticator implements WebappAuthenticator {
private static final Log log = LogFactory.getLog(OneTimeTokenAuthenticator.class);
@Override
public void init() {
}
public boolean canHandle(org.apache.catalina.connector.Request request) {
return request.getHeader(Constants.HTTPHeaders.ONE_TIME_TOKEN_HEADER) != null;
}
public AuthenticationInfo authenticate(org.apache.catalina.connector.Request request, Response response) {
AuthenticationInfo authenticationInfo = new AuthenticationInfo();
try {
OTPManagementService otpManagementService = AuthenticatorFrameworkDataHolder.getInstance()
.getOtpManagementService();
OneTimePinDTO validOTP = otpManagementService.isValidOTP(request.getHeader(Constants.HTTPHeaders
.ONE_TIME_TOKEN_HEADER));
if (validOTP != null) {
authenticationInfo.setStatus(Status.CONTINUE);
authenticationInfo.setTenantId(validOTP.getTenantId());
authenticationInfo.setTenantDomain(Utils.getTenantDomain(validOTP.getTenantId()));
authenticationInfo.setUsername(validOTP.getUsername());
} else {
authenticationInfo.setStatus(Status.FAILURE);
authenticationInfo.setMessage("Invalid OTP token.");
}
} catch (Exception e) {
String msg = "OTP Token Validation Failed.";
log.error(msg, e);
authenticationInfo.setStatus(Status.FAILURE);
authenticationInfo.setMessage(msg);
}
return authenticationInfo;
}
public String getName() {
return "One-Time-Token";
}
@Override
public void setProperties(Properties properties) {
}
@Override
public Properties getProperties() {
return null;
}
@Override
public String getProperty(String name) {
return null;
}
}

@ -20,6 +20,7 @@ package org.wso2.carbon.webapp.authenticator.framework.internal;
import org.wso2.carbon.certificate.mgt.core.scep.SCEPManager; import org.wso2.carbon.certificate.mgt.core.scep.SCEPManager;
import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService; import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService;
import org.wso2.carbon.registry.core.service.TenantRegistryLoader; import org.wso2.carbon.registry.core.service.TenantRegistryLoader;
import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader; import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader;
@ -35,6 +36,7 @@ public class AuthenticatorFrameworkDataHolder {
private OAuth2TokenValidationService oAuth2TokenValidationService; private OAuth2TokenValidationService oAuth2TokenValidationService;
private TenantIndexingLoader tenantIndexingLoader; private TenantIndexingLoader tenantIndexingLoader;
private TenantRegistryLoader tenantRegistryLoader; private TenantRegistryLoader tenantRegistryLoader;
private OTPManagementService otpManagementService;
private static AuthenticatorFrameworkDataHolder private static AuthenticatorFrameworkDataHolder
thisInstance = new AuthenticatorFrameworkDataHolder(); thisInstance = new AuthenticatorFrameworkDataHolder();
@ -114,4 +116,10 @@ public class AuthenticatorFrameworkDataHolder {
public TenantRegistryLoader getTenantRegistryLoader() { public TenantRegistryLoader getTenantRegistryLoader() {
return tenantRegistryLoader; return tenantRegistryLoader;
} }
public OTPManagementService getOtpManagementService() { return otpManagementService; }
public void setOtpManagementService(OTPManagementService otpManagementService) {
this.otpManagementService = otpManagementService;
}
} }

@ -25,6 +25,7 @@ import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext; import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.certificate.mgt.core.scep.SCEPManager; import org.wso2.carbon.certificate.mgt.core.scep.SCEPManager;
import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService; import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService;
import org.wso2.carbon.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService;
import org.wso2.carbon.registry.core.service.TenantRegistryLoader; import org.wso2.carbon.registry.core.service.TenantRegistryLoader;
import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader; import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader;
@ -80,7 +81,14 @@ import java.util.Properties;
* cardinality="1..1" policy="dynamic" * cardinality="1..1" policy="dynamic"
* bind="setTenantRegistryLoader" * bind="setTenantRegistryLoader"
* unbind="unsetTenantRegistryLoader" * unbind="unsetTenantRegistryLoader"
* @scr.reference name="org.wso2.carbon.device.manager"
* interface="org.wso2.carbon.device.mgt.common.spi.OTPManagementService"
* cardinality="1..1"
* policy="dynamic"
* bind="setOTPManagementService"
* unbind="unsetOTPManagementService"
*/ */
public class WebappAuthenticatorFrameworkServiceComponent { public class WebappAuthenticatorFrameworkServiceComponent {
private static final Log log = LogFactory.getLog(WebappAuthenticatorFrameworkServiceComponent.class); private static final Log log = LogFactory.getLog(WebappAuthenticatorFrameworkServiceComponent.class);
@ -211,4 +219,18 @@ public class WebappAuthenticatorFrameworkServiceComponent {
protected void unsetTenantRegistryLoader(TenantRegistryLoader tenantRegistryLoader) { protected void unsetTenantRegistryLoader(TenantRegistryLoader tenantRegistryLoader) {
AuthenticatorFrameworkDataHolder.getInstance().setTenantRegistryLoader(null); AuthenticatorFrameworkDataHolder.getInstance().setTenantRegistryLoader(null);
} }
protected void setOTPManagementService(OTPManagementService otpManagementService) {
if (log.isDebugEnabled()) {
log.debug("Setting OTP Management OSGI Service");
}
AuthenticatorFrameworkDataHolder.getInstance().setOtpManagementService(otpManagementService);
}
protected void unsetOTPManagementService(OTPManagementService otpManagementService) {
if (log.isDebugEnabled()) {
log.debug("Removing OTP Management OSGI Service");
}
AuthenticatorFrameworkDataHolder.getInstance().setOtpManagementService(null);
}
} }

@ -24,5 +24,9 @@
<Name>CertificateAuth</Name> <Name>CertificateAuth</Name>
<ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.CertificateAuthenticator</ClassName> <ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.CertificateAuthenticator</ClassName>
</Authenticator> </Authenticator>
<Authenticator>
<Name>OTPAuth</Name>
<ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.OneTimeTokenAuthenticator</ClassName>
</Authenticator>
</Authenticators> </Authenticators>
</WebappAuthenticatorConfig> </WebappAuthenticatorConfig>

@ -572,6 +572,23 @@ CREATE TABLE IF NOT EXISTS DM_METADATA (
); );
-- END OF METADATA TABLE -- -- END OF METADATA TABLE --
-- DM_OTP_DATA TABLE --
CREATE TABLE IF NOT EXISTS DM_OTP_DATA (
ID INT AUTO_INCREMENT NOT NULL,
OTP_TOKEN VARCHAR(100) NOT NULL,
TENANT_ID INT NOT NULL,
USERNAME VARCHAR(500) DEFAULT NOT NULL,
EMAIL VARCHAR(100) NOT NULL,
EMAIL_TYPE VARCHAR(20) NOT NULL,
META_INFO VARCHAR(20000) NOT NULL,
CREATED_AT TIMESTAMP NOT NULL,
EXPIRY_TIME INT NOT NULL DEFAULT 3600,
IS_EXPIRED BOOLEAN DEFAULT false,
PRIMARY KEY (ID),
CONSTRAINT email_type_uk UNIQUE (EMAIL, EMAIL_TYPE)
);
-- END OF DM_OTP_DATA TABLE --
-- DASHBOARD RELATED VIEWS -- -- DASHBOARD RELATED VIEWS --
CREATE VIEW POLICY_COMPLIANCE_INFO AS CREATE VIEW POLICY_COMPLIANCE_INFO AS
SELECT SELECT

@ -614,6 +614,23 @@ CREATE TABLE DM_METADATA (
); );
-- END OF METADATA TABLE -- -- END OF METADATA TABLE --
-- DM_OTP_DATA TABLE --
CREATE TABLE DM_OTP_DATA (
ID INT IDENTITY NOT NULL,
OTP_TOKEN VARCHAR(100) NOT NULL,
TENANT_ID INTEGER NOT NULL,
USERNAME VARCHAR(500) NOT NULL,
EMAIL VARCHAR(100) NOT NULL,
EMAIL_TYPE VARCHAR(20) NOT NULL,
META_INFO VARCHAR(20000) NOT NULL,
CREATED_AT DATETIME2(0) NOT NULL,
EXPIRY_TIME INT NOT NULL DEFAULT 3600,
IS_EXPIRED BIT DEFAULT false,
PRIMARY KEY (ID),
CONSTRAINT email_type_uk UNIQUE (EMAIL, EMAIL_TYPE)
);
-- END OF DM_OTP_DATA TABLE --
-- DASHBOARD RELATED VIEWS -- -- DASHBOARD RELATED VIEWS --
IF NOT EXISTS (SELECT * FROM SYS.VIEWS WHERE NAME = 'POLICY_COMPLIANCE_INFO') IF NOT EXISTS (SELECT * FROM SYS.VIEWS WHERE NAME = 'POLICY_COMPLIANCE_INFO')

@ -630,6 +630,23 @@ CREATE TABLE IF NOT EXISTS DM_METADATA (
) ENGINE=InnoDB; ) ENGINE=InnoDB;
-- END OF METADATA TABLE -- -- END OF METADATA TABLE --
-- DM_OTP_DATA TABLE --
CREATE TABLE IF NOT EXISTS DM_OTP_DATA (
ID INT AUTO_INCREMENT NOT NULL,
OTP_TOKEN VARCHAR(100) NOT NULL,
TENANT_ID INT NOT NULL,
USERNAME VARCHAR(500) NOT NULL,
EMAIL VARCHAR(100) NOT NULL,
EMAIL_TYPE VARCHAR(20) NOT NULL,
META_INFO VARCHAR(20000) NOT NULL,
CREATED_AT TIMESTAMP NOT NULL,
EXPIRY_TIME INT NOT NULL DEFAULT 3600,
IS_EXPIRED BOOLEAN DEFAULT false,
PRIMARY KEY (ID),
CONSTRAINT email_type_uk UNIQUE (EMAIL, EMAIL_TYPE)
);
-- END OF DM_OTP_DATA TABLE --
-- DASHBOARD RELATED VIEWS -- -- DASHBOARD RELATED VIEWS --
CREATE VIEW DEVICE_INFO_VIEW AS CREATE VIEW DEVICE_INFO_VIEW AS

@ -978,6 +978,34 @@ END;
/ /
-- END OF METADATA TABLE -- -- END OF METADATA TABLE --
-- OPT-DATA TABLE --
CREATE TABLE DM_OTP_DATA (
ID NUMBER(10) NOT NULL,
OTP_TOKEN VARCHAR2(100) NOT NULL,
TENANT_ID INTEGER NOT NULL,
USERNAME VARCHAR(500) NOT NULL,
EMAIL VARCHAR2(100) NOT NULL,
EMAIL_TYPE VARCHAR2(20) NOT NULL,
META_INFO VARCHAR2(20000) NOT NULL,
CREATED_AT TIMESTAMP(0) NOT NULL,
EXPIRY_TIME NUMBER(10) DEFAULT 3600 NOT NULL,
IS_EXPIRED CHAR(1) DEFAULT false,
PRIMARY KEY (ID),
CONSTRAINT email_type_uk UNIQUE (EMAIL, EMAIL_TYPE)
);
-- Generate ID using sequence and trigger
CREATE SEQUENCE DM_OTP_DATA_seq START WITH 1 INCREMENT BY 1;
CREATE OR REPLACE TRIGGER DM_OTP_DATA_seq_tr
BEFORE INSERT ON DM_OTP_DATA FOR EACH ROW
WHEN (NEW.ID IS NULL)
BEGIN
SELECT DM_OTP_DATA_seq.NEXTVAL INTO :NEW.ID FROM DUAL;
END;
/
-- END OF OTP-DATA TABLE --
-- DASHBOARD RELATED VIEWS -- -- DASHBOARD RELATED VIEWS --
CREATE VIEW POLICY_COMPLIANCE_INFO AS CREATE VIEW POLICY_COMPLIANCE_INFO AS

@ -617,6 +617,26 @@ CREATE TABLE IF NOT EXISTS DM_METADATA (
); );
-- END OF METADATA TABLE -- -- END OF METADATA TABLE --
-- OPT-DATA TABLE --
CREATE SEQUENCE DM_OTP_DATA_seq;
CREATE TABLE IF NOT EXISTS DM_OTP_DATA (
ID INT DEFAULT NEXTVAL ('DM_OTP_DATA_seq') NOT NULL,
OTP_TOKEN VARCHAR(100) NOT NULL,
TENANT_ID INTEGER NOT NULL,
USERNAME VARCHAR(500) NOT NULL,
EMAIL VARCHAR(100) NOT NULL,
EMAIL_TYPE VARCHAR(20) NOT NULL,
META_INFO VARCHAR(20000) NOT NULL,
CREATED_AT TIMESTAMP(0) NOT NULL,
EXPIRY_TIME INT NOT NULL DEFAULT 3600,
IS_EXPIRED BOOLEAN DEFAULT false,
PRIMARY KEY (ID),
CONSTRAINT email_type_uk UNIQUE (EMAIL, EMAIL_TYPE)
);
-- END OF OPT-DATA TABLE --
-- DASHBOARD RELATED VIEWS -- -- DASHBOARD RELATED VIEWS --
CREATE VIEW DEVICE_INFO_VIEW AS CREATE VIEW DEVICE_INFO_VIEW AS

@ -14,6 +14,22 @@
KIND, either express or implied. See the License for the KIND, either express or implied. See the License for the
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*# *#
<EmailConfig> <EmailConfig>
<Subject>You have been invited to enroll your device in Entgra IoT</Subject> <Subject>You have been invited to enroll your device in Entgra IoT</Subject>
@ -37,7 +53,13 @@
</p> </p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"> <p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
You have been invited to enrol your device in Entgra IoT Server. You have been invited to enrol your device in Entgra IoT Server.
Click <a href="$base-url-https/devicemgt/device/enroll">here</a> to begin device enrolment.</p> Click <a href="$base-url-https/endpoint-mgt/devices/enroll?token=$otp-token">here</a> to begin device enrolment.</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Enrollment Steps are as below,
</p>
$enrollment-steps
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"> <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. Should you need assistance, please contact your administrator.

@ -0,0 +1,61 @@
#*
Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*#
<EmailConfig>
<Subject>Verify email to register with Entgra IoTS</Subject>
<Body>
<![CDATA[
<html>
<head>
<title>Entgra IoT Server</title>
</head>
<body style="color: #666666; background-color:#cdcdcd; padding: 0px; margin: 0px;">
<div style="background-color:#cdcdcd; font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; padding: 20px 0px; margin: 0px;">
<div style="width: 86%; max-width: 650px; padding: 2%; background-color: #ffffff; margin: auto; border-radius: 14px;">
<div style="background-color: #ffebcc; line-height: 0px; border-top-left-radius: 10px; border-top-right-radius: 10px; padding: 10px;">
<div style="display: inline-block; line-height: 0px;">
<img alt="entgra" src="https://storage.googleapis.com/cdn-entgra/logo.png" height="50px" width="143px" />
</div>
</div>
<div style="background-color: #ffffff; line-height: 170%; color: #666666; padding: 20px 25px;">
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px 20px;">
Hi $first-name,
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Congratulations!!! Thank you for registering with Entgra cloud. Please click and log in to the
following link to complete your registration with us. Click <a href="$base-url-https/self-register/sign-up?token=$otp-token">here</a>.
</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
If you need further 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>

@ -0,0 +1,98 @@
#*
Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except
in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*#
<EmailConfig>
<Subject>Welcome to Entgra Evaluation Cloud</Subject>
<Body>
<![CDATA[
<html>
<head>
<title>Entgra IoT Server</title>
</head>
<body style="color: #666666; background-color:#cdcdcd; padding: 0px; margin: 0px;">
<div style="background-color:#cdcdcd; font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; padding: 20px 0px; margin: 0px;">
<div style="width: 86%; max-width: 650px; padding: 2%; background-color: #ffffff; margin: auto; border-radius: 14px;">
<div style="background-color: #ffebcc; line-height: 0px; border-top-left-radius: 10px; border-top-right-radius: 10px; padding: 10px;">
<div style="display: inline-block; line-height: 0px;">
<img alt="entgra" src="https://storage.googleapis.com/cdn-entgra/logo.png" height="50px" width="143px" />
</div>
</div>
<div style="background-color: #ffffff; line-height: 170%; color: #666666; padding: 20px 25px;">
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px 20px;">
Hi $first-name,
</p>
<div>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
Welcome to Entgra Evaluation Cloud!! Entgra server support offers managing Android, iOS and Windows
devices along with a wide range of features that support cooperate (COPE) or personal device (BYOD)
enrollments.
</p>
</div>
<div>
<h3>Access different portals</h3>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">Your log-in credentials to any of our portals(endpoint-mgt, store, publisher) are,</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Username: </b> $portal-username</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Password: </b> password provided at registration for an evaluation account.</p>
<h4>Endpoint management portal URL: <a href="$base-url-https/endpoint-mgt/">$base-url-https/endpoint-mgt/</a></h4>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
This is the portal used to send operations and policies to devices and overall management of
the server.
</p>
<h4>Application store portal URL: <a href="$base-url-https/store">$base-url-https/store</a></h4>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
This is an in-house corporate app store where you can host all your corporate applications.
Users may browse apps and install them to their devices if the administrator has made the apps
publicly visible to users. Administrator can install, uninstall and update apps in the user
device or device groups.
</p>
<h4>Application publishing portal URL: <a href="$base-url-https/publisher">$base-url-https/publisher</a></h4>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
The portal for publishing new applications for internal use. This is the developer view of the
enterprise application store that comes with the product.
</p>
</div>
<div>
<h3>Enroll a device</h3>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
Please find here a set of videos on how to enroll and onboard devices to our Cloud platform.
<a href="https://entgra-documentation.gitlab.io/v4.0.0/docs/product-guide/enrollment-guide/enroll-cloud/">[Cloud Enrollment Guide]</a>
When enrolling a device, Make sure to use the following log-in format:
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Organisation:</b> $tenant-domain</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Username:</b> $agent-username</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;"><b>Password:</b> password provided at registration.</p>
</div>
<div>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
If you have any further questions, please reach out to us using your registered mail to
<a href="bizdev-group@entgra.io"> bizdev-group@entgra.io.</a> Looking forward to working with you.
</p>
<p style="font-length: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 20px 0px 5px;">
Best Regards,
</p>
<p style="font-size: 1em; font-family: Arial, Helvetica; line-height: 170%; color: #666666; margin: 5px 0px;">
Entgra Cloud Team
</p>
</div>
</div>
</div>
</div>
</body>
</html>
]]>
</Body>
</EmailConfig>

@ -31,6 +31,10 @@
<Name>CertificateAuth</Name> <Name>CertificateAuth</Name>
<ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.CertificateAuthenticator</ClassName> <ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.CertificateAuthenticator</ClassName>
</Authenticator> </Authenticator>
<Authenticator>
<Name>OTPAuth</Name>
<ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.OneTimeTokenAuthenticator</ClassName>
</Authenticator>
<Authenticator> <Authenticator>
<Name>BST</Name> <Name>BST</Name>
<ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.BSTAuthenticator</ClassName> <ClassName>org.wso2.carbon.webapp.authenticator.framework.authenticator.BSTAuthenticator</ClassName>

@ -1772,6 +1772,11 @@
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven.checkstyle.vesion}</version> <version>${maven.checkstyle.vesion}</version>
</dependency> </dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>${apache.validator.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@ -2219,6 +2224,9 @@
<!--apache osgi mock version--> <!--apache osgi mock version-->
<apache.osgi.mock.version>2.3.2</apache.osgi.mock.version> <apache.osgi.mock.version>2.3.2</apache.osgi.mock.version>
<!--apache validator version-->
<apache.validator.version>1.7</apache.validator.version>
<!-- api-mgt handler version properties --> <!-- api-mgt handler version properties -->
<org.apache.synapse.version>2.1.7-wso2v7</org.apache.synapse.version> <org.apache.synapse.version>2.1.7-wso2v7</org.apache.synapse.version>
<org.apache.ws.security.wso2.version>1.5.11.wso2v15</org.apache.ws.security.wso2.version> <org.apache.ws.security.wso2.version>1.5.11.wso2v15</org.apache.ws.security.wso2.version>
@ -2252,7 +2260,7 @@
<node.version>v12.18.1</node.version> <node.version>v12.18.1</node.version>
<maven.checkstyle.vesion>3.1.0</maven.checkstyle.vesion> <maven.checkstyle.vesion>3.1.0</maven.checkstyle.vesion>
<!--websocket related lib versions--> <!--websocket related lib versions-->
<tomcat.websocket.version>7.0.85</tomcat.websocket.version> <tomcat.websocket.version>7.0.85</tomcat.websocket.version>
<javax.websocket.version>1.0</javax.websocket.version> <javax.websocket.version>1.0</javax.websocket.version>

Loading…
Cancel
Save