Merge pull request 'Sync OTP enrollment and mutual SSL related features' (#149) from pahansith/device-mgt-core:sync-master into master

Reviewed-on: community/device-mgt-core#149
vpp^2
commit 99e760278c

@ -25,6 +25,15 @@ public class Certificate {
X509Certificate certificate; X509Certificate certificate;
int tenantId; int tenantId;
String tenantDomain; String tenantDomain;
String deviceIdentifier;
public String getDeviceIdentifier() {
return deviceIdentifier;
}
public void setDeviceIdentifier(String deviceIdentifier) {
this.deviceIdentifier = deviceIdentifier;
}
public int getTenantId() { public int getTenantId() {
return tenantId; return tenantId;

@ -41,6 +41,17 @@ public interface CertificateDAO {
void addCertificate(List<Certificate> certificate) void addCertificate(List<Certificate> certificate)
throws CertificateManagementDAOException; throws CertificateManagementDAOException;
/**
* This can be used to store a certificate in the database, where it will be stored against the serial number
* of the certificate.
*
* @param certificate Holds the certificate and relevant details.
* @throws CertificateManagementDAOException
*
*/
void addCertificate(Certificate certificate)
throws CertificateManagementDAOException;
/** /**
* Usage is to obtain a certificate stored in the database by providing the common name. * Usage is to obtain a certificate stored in the database by providing the common name.
* *
@ -51,6 +62,16 @@ public interface CertificateDAO {
*/ */
CertificateResponse retrieveCertificate(String serialNumber) throws CertificateManagementDAOException; CertificateResponse retrieveCertificate(String serialNumber) throws CertificateManagementDAOException;
/**
* Obtain a certificated stored in the database by providing the common name and the tenant ID
*
* @param serialNumber Serial number (Common name) of the certificate
* @param tenantId ID of the certificate owning tenant
* @return representation of the certificate.
* @throws CertificateManagementDAOException if fails to read the certificate from the database
*/
CertificateResponse retrieveCertificate(String serialNumber, int tenantId) throws CertificateManagementDAOException;
/** /**
* Get all the certificates in a paginated manner. * Get all the certificates in a paginated manner.
* *

@ -81,6 +81,40 @@ public abstract class AbstractCertificateDAOImpl implements CertificateDAO{
} }
} }
@Override
public void addCertificate(Certificate certificate)
throws CertificateManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
try {
conn = this.getConnection();
stmt = conn.prepareStatement(
"INSERT INTO DM_DEVICE_CERTIFICATE (SERIAL_NUMBER, CERTIFICATE, TENANT_ID," +
" USERNAME, DEVICE_IDENTIFIER) VALUES (?,?,?,?,?)");
PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.
getThreadLocalCarbonContext();
String username = threadLocalCarbonContext.getUsername();
// the serial number of the certificate used for its creation is set as its alias.
String serialNumber = certificate.getSerial();
if (serialNumber == null || serialNumber.isEmpty()) {
serialNumber = String.valueOf(certificate.getCertificate().getSerialNumber());
}
byte[] bytes = Serializer.serialize(certificate.getCertificate());
stmt.setString(1, serialNumber);
stmt.setBytes(2, bytes);
stmt.setInt(3, certificate.getTenantId());
stmt.setString(4, username);
stmt.setString(5, certificate.getDeviceIdentifier());
stmt.executeUpdate();
} catch (SQLException | IOException e) {
throw new CertificateManagementDAOException("Error occurred while saving the " +
"certificate. ", e);
} finally {
CertificateManagementDAOUtil.cleanupResources(stmt, null);
}
}
@Override @Override
public CertificateResponse retrieveCertificate(String serialNumber) public CertificateResponse retrieveCertificate(String serialNumber)
throws CertificateManagementDAOException { throws CertificateManagementDAOException {
@ -119,6 +153,42 @@ public abstract class AbstractCertificateDAOImpl implements CertificateDAO{
return certificateResponse; return certificateResponse;
} }
@Override
public CertificateResponse retrieveCertificate(String serialNumber, int tenantId) throws CertificateManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
ResultSet resultSet = null;
CertificateResponse certificateResponse = null;
try {
conn = this.getConnection();
String query =
"SELECT CERTIFICATE, SERIAL_NUMBER, TENANT_ID, USERNAME FROM"
+ " DM_DEVICE_CERTIFICATE WHERE SERIAL_NUMBER = ? AND TENANT_ID = ? ";
stmt = conn.prepareStatement(query);
stmt.setString(1, serialNumber);
stmt.setInt(2, tenantId);
resultSet = stmt.executeQuery();
if (resultSet.next()) {
certificateResponse = new CertificateResponse();
byte[] certificateBytes = resultSet.getBytes("CERTIFICATE");
certificateResponse.setCertificate(certificateBytes);
certificateResponse.setSerialNumber(resultSet.getString("SERIAL_NUMBER"));
certificateResponse.setTenantId(resultSet.getInt("TENANT_ID"));
certificateResponse.setUsername(resultSet.getString("USERNAME"));
CertificateGenerator.extractCertificateDetails(certificateBytes, certificateResponse);
}
} catch (SQLException e) {
String errorMsg =
"Unable to get the read the certificate with serial" + serialNumber;
log.error(errorMsg, e);
throw new CertificateManagementDAOException(errorMsg, e);
} finally {
CertificateManagementDAOUtil.cleanupResources(stmt, resultSet);
}
return certificateResponse;
}
@Override @Override
public List<CertificateResponse> searchCertificate(String serialNumber) public List<CertificateResponse> searchCertificate(String serialNumber)
throws CertificateManagementDAOException { throws CertificateManagementDAOException {

@ -358,15 +358,31 @@ public class CertificateGenerator {
CertificateResponse lookUpCertificate = null; CertificateResponse lookUpCertificate = null;
KeyStoreReader keyStoreReader = new KeyStoreReader(); KeyStoreReader keyStoreReader = new KeyStoreReader();
if (distinguishedName != null && !distinguishedName.isEmpty()) { if (distinguishedName != null && !distinguishedName.isEmpty()) {
if (distinguishedName.contains("/CN=")) { if (distinguishedName.contains("CN=")) {
String[] dnSplits = distinguishedName.split("/"); String[] dnSplits = null;
for (String dnPart : dnSplits) { if (distinguishedName.contains("/")) {
if (dnPart.contains("CN=")) { dnSplits = distinguishedName.split("/");
String commonNameExtracted = dnPart.replace("CN=", ""); } else if (distinguishedName.contains(",")) {
lookUpCertificate = keyStoreReader.getCertificateBySerial(commonNameExtracted); //some older versions of nginx will forward the client certificate subject dn separated with commas
break; dnSplits = distinguishedName.split(",");
}
String commonNameExtracted = null;
int tenantId = 0;
if (dnSplits != null && dnSplits.length >= 1) {
for (String dnPart : dnSplits) {
if (dnPart.contains("CN=")) {
commonNameExtracted = dnPart.replace("CN=", "");
} else if (dnPart.contains("OU=")) {
//the OU of the certificate will be like OU=tenant_<TENANT_ID> ex: OU=tenant_-1234
//splitting by underscore to extract the tenant domain
String[] orgUnitSplits = dnPart.split("_");
tenantId = Integer.parseInt(orgUnitSplits[1]);
}
} }
} }
lookUpCertificate = keyStoreReader.getCertificateBySerial(commonNameExtracted, tenantId);
} else { } else {
LdapName ldapName; LdapName ldapName;
try { try {
@ -694,6 +710,30 @@ public class CertificateGenerator {
} }
} }
public void saveCertificate(io.entgra.device.mgt.core.certificate.mgt.core.bean.Certificate
certificate) throws KeystoreException {
if (certificate == null) {
return;
}
try {
CertificateDAO certificateDAO = CertificateManagementDAOFactory.getCertificateDAO();
CertificateManagementDAOFactory.beginTransaction();
certificateDAO.addCertificate(certificate);
CertificateManagementDAOFactory.commitTransaction();
} catch (CertificateManagementDAOException e) {
String errorMsg = "Error occurred when saving the generated certificate in database";
log.error(errorMsg);
CertificateManagementDAOFactory.rollbackTransaction();
throw new KeystoreException(errorMsg, e);
} catch (TransactionManagementException e) {
String errorMsg = "Error occurred when saving the generated certificate in database";
log.error(errorMsg);
throw new KeystoreException(errorMsg, e);
}
}
public void saveCertInKeyStore(List<io.entgra.device.mgt.core.certificate.mgt.core.bean.Certificate> certificate) public void saveCertInKeyStore(List<io.entgra.device.mgt.core.certificate.mgt.core.bean.Certificate> certificate)
throws KeystoreException { throws KeystoreException {

@ -275,6 +275,43 @@ public class KeyStoreReader {
return raPrivateKey; return raPrivateKey;
} }
public CertificateResponse getCertificateBySerial(String serialNumber, int tenantId) throws KeystoreException {
CertificateResponse certificateResponse = null;
try {
CertificateCacheManager cacheManager = CertificateCacheManagerImpl.getInstance();
certificateResponse = cacheManager.getCertificateBySerial(serialNumber);
if (certificateResponse == null) {
try {
CertificateManagementDAOFactory.openConnection();
certificateResponse = certDao.retrieveCertificate(serialNumber, tenantId);
} catch (SQLException e) {
String errorMsg = "Error when making a connection to the database.";
throw new KeystoreException(errorMsg, e);
} finally {
CertificateManagementDAOFactory.closeConnection();
}
if (certificateResponse != null && certificateResponse.getCertificate() != null) {
Certificate certificate = (Certificate) Serializer.deserialize(certificateResponse.getCertificate());
if (certificate instanceof X509Certificate) {
X509Certificate x509cert = (X509Certificate) certificate;
String commonName = CertificateGenerator.getCommonName(x509cert);
certificateResponse.setCommonName(commonName);
cacheManager.addCertificateBySerial(serialNumber, certificateResponse);
}
}
}
} catch (CertificateManagementDAOException e) {
String errorMsg = "Error when retrieving certificate from the the database for the serial number: " +
serialNumber;
throw new KeystoreException(errorMsg, e);
} catch (ClassNotFoundException | IOException e) {
String errorMsg = "Error when de-serializing saved certificate.";
throw new KeystoreException(errorMsg, e);
}
return certificateResponse;
}
public CertificateResponse getCertificateBySerial(String serialNumber) throws KeystoreException { public CertificateResponse getCertificateBySerial(String serialNumber) throws KeystoreException {
CertificateResponse certificateResponse = null; CertificateResponse certificateResponse = null;
try { try {

@ -134,6 +134,8 @@ public final class DeviceManagementConstants {
public static final String LAST_NAME = "last-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_USERNAME = "tenant-admin-username";
public static final String TENANT_ADMIN_PASSWORD = "tenant-admin-password"; public static final String TENANT_ADMIN_PASSWORD = "tenant-admin-password";
public static final int OTP_DEFAULT_EXPIRY_SECONDS = 3600;
} }
public static final class EventServices { public static final class EventServices {

@ -25,6 +25,7 @@ public class QREnrollmentDetails {
String username; String username;
String enrollmentMode; String enrollmentMode;
Map<String, String> customValues; Map<String, String> customValues;
int tokenExpiry;
public Map<String, String> getCustomValues() { public Map<String, String> getCustomValues() {
return customValues; return customValues;
@ -45,4 +46,12 @@ public class QREnrollmentDetails {
public String getEnrollmentMode() { return enrollmentMode; } public String getEnrollmentMode() { return enrollmentMode; }
public void setEnrollmentMode(String enrollmentMode) { this.enrollmentMode = enrollmentMode; } public void setEnrollmentMode(String enrollmentMode) { this.enrollmentMode = enrollmentMode; }
public int getTokenExpiry() {
return tokenExpiry;
}
public void setTokenExpiry(int tokenExpiry) {
this.tokenExpiry = tokenExpiry;
}
} }

@ -19,5 +19,5 @@
package io.entgra.device.mgt.core.device.mgt.common.otp.mgt; package io.entgra.device.mgt.core.device.mgt.common.otp.mgt;
public enum OTPEmailTypes { public enum OTPEmailTypes {
USER_VERIFY, DEVICE_ENROLLMENT USER_VERIFY, DEVICE_ENROLLMENT, USER_INVITE, REMOTE_SESSION
} }

@ -35,7 +35,8 @@ public interface OTPManagementService {
* @throws OTPManagementException if error occurred whle verifying validity of the OPT * @throws OTPManagementException if error occurred whle verifying validity of the OPT
* @throws BadRequestException if found an null value for OTP * @throws BadRequestException if found an null value for OTP
*/ */
OneTimePinDTO isValidOTP(String oneTimeToken) throws OTPManagementException, BadRequestException; OneTimePinDTO isValidOTP(String oneTimeToken, boolean requireRenewal) throws
OTPManagementException, BadRequestException;
/** /**
* Invalidate the OTP and send welcome mail * Invalidate the OTP and send welcome mail
@ -59,8 +60,7 @@ public interface OTPManagementService {
boolean hasEmailRegistered(String email, String emailDomain) throws OTPManagementException, boolean hasEmailRegistered(String email, String emailDomain) throws OTPManagementException,
DeviceManagementException; DeviceManagementException;
OneTimePinDTO generateOneTimePin(String email, String emailType, String userName, Object metaDataObj, OneTimePinDTO generateOneTimePin(OneTimePinDTO oneTimePinData, boolean persistPin) throws OTPManagementException;
int tenantId, boolean persistPin) throws OTPManagementException;
OneTimePinDTO getRenewedOtpByEmailAndMailType(String email, String emailType) throws OTPManagementException; OneTimePinDTO getRenewedOtpByEmailAndMailType(String email, String emailType) throws OTPManagementException;

@ -156,7 +156,12 @@ public interface DeviceManagementConfigService {
value = "The properties list using for query a device", value = "The properties list using for query a device",
required = true) required = true)
@QueryParam("properties") @QueryParam("properties")
String properties); String properties,
@ApiParam(
name = "withAccessToken",
value = "Whether to use access token or otp token for device configuration")
@QueryParam("withAccessToken")
boolean withAccessToken);
@PUT @PUT
@Path("/device/transfer") @Path("/device/transfer")

@ -21,6 +21,10 @@ import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.OTPManagementException;
import io.entgra.device.mgt.core.device.mgt.common.otp.mgt.OTPEmailTypes;
import io.entgra.device.mgt.core.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import io.entgra.device.mgt.core.device.mgt.common.spi.OTPManagementService;
import io.entgra.device.mgt.core.device.mgt.config.api.beans.ErrorResponse; import io.entgra.device.mgt.core.device.mgt.config.api.beans.ErrorResponse;
import io.entgra.device.mgt.core.device.mgt.config.api.service.DeviceManagementConfigService; import io.entgra.device.mgt.core.device.mgt.config.api.service.DeviceManagementConfigService;
import io.entgra.device.mgt.core.device.mgt.config.api.util.DeviceMgtAPIUtils; import io.entgra.device.mgt.core.device.mgt.config.api.util.DeviceMgtAPIUtils;
@ -77,7 +81,8 @@ public class DeviceManagementConfigServiceImpl implements DeviceManagementConfig
@Path("/configurations") @Path("/configurations")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getConfiguration(@HeaderParam("token") String token, public Response getConfiguration(@HeaderParam("token") String token,
@QueryParam("properties") String properties) { @QueryParam("properties") String properties,
@QueryParam("withAccessToken") boolean withAccessToken) {
DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService(); DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService();
try { try {
if (token == null || token.isEmpty()) { if (token == null || token.isEmpty()) {
@ -102,7 +107,8 @@ public class DeviceManagementConfigServiceImpl implements DeviceManagementConfig
deviceProps.put("token", token); deviceProps.put("token", token);
DeviceConfiguration devicesConfiguration = DeviceConfiguration devicesConfiguration =
dms.getDeviceConfiguration(deviceProps); dms.getDeviceConfiguration(deviceProps);
setAccessTokenToDeviceConfigurations(devicesConfiguration); if (withAccessToken) setAccessTokenToDeviceConfigurations(devicesConfiguration);
else setOTPTokenToDeviceConfigurations(devicesConfiguration);
return Response.status(Response.Status.OK).entity(devicesConfiguration).build(); return Response.status(Response.Status.OK).entity(devicesConfiguration).build();
} catch (DeviceManagementException e) { } catch (DeviceManagementException e) {
String msg = "Error occurred while retrieving configurations"; String msg = "Error occurred while retrieving configurations";
@ -214,6 +220,33 @@ public class DeviceManagementConfigServiceImpl implements DeviceManagementConfig
} }
} }
private void setOTPTokenToDeviceConfigurations(DeviceConfiguration deviceConfiguration)
throws DeviceManagementException {
OneTimePinDTO oneTimePinData = new OneTimePinDTO();
oneTimePinData.setEmail(OTPEmailTypes.DEVICE_ENROLLMENT.toString());
oneTimePinData.setEmailType(OTPEmailTypes.DEVICE_ENROLLMENT.toString());
oneTimePinData.setUsername(deviceConfiguration.getDeviceOwner());
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
deviceConfiguration.getTenantDomain(), true);
oneTimePinData.setTenantId(PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId());
PrivilegedCarbonContext.endTenantFlow();
OTPManagementService otpManagementService = DeviceMgtAPIUtils.getOtpManagementService();
try {
OneTimePinDTO oneTimePinDTO = otpManagementService.generateOneTimePin(oneTimePinData, true);
if (oneTimePinDTO == null) {
String msg = "Null value returned when generating OTP token for " + oneTimePinData.getOtpToken();
log.error(msg);
throw new DeviceManagementException(msg);
}
deviceConfiguration.setAccessToken(oneTimePinDTO.getOtpToken());
} catch (OTPManagementException ex) {
String msg = "Error occurred while generating one time pin: " + ex.getMessage();
log.error(msg, ex);
throw new DeviceManagementException(msg, ex);
}
}
@Override @Override
@Path("/tenants") @Path("/tenants")
@GET @GET

@ -21,6 +21,7 @@ package io.entgra.device.mgt.core.device.mgt.config.api.util;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext;
import io.entgra.device.mgt.core.device.mgt.common.spi.OTPManagementService;
import io.entgra.device.mgt.core.device.mgt.core.service.DeviceManagementProviderService; import io.entgra.device.mgt.core.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.service.RealmService;
@ -34,6 +35,8 @@ public class DeviceMgtAPIUtils {
private static DeviceManagementProviderService deviceManagementProviderService = null; private static DeviceManagementProviderService deviceManagementProviderService = null;
private static RealmService realmService = null; private static RealmService realmService = null;
private static OTPManagementService otpManagementService = null;
public static DeviceManagementProviderService getDeviceManagementService() { public static DeviceManagementProviderService getDeviceManagementService() {
if (deviceManagementProviderService == null) { if (deviceManagementProviderService == null) {
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
@ -48,6 +51,19 @@ public class DeviceMgtAPIUtils {
return deviceManagementProviderService; return deviceManagementProviderService;
} }
public static 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 RealmService getRealmService() { public static RealmService getRealmService() {
if (realmService == null) { if (realmService == null) {
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();

@ -18,6 +18,7 @@
package io.entgra.device.mgt.core.device.mgt.core.otp.mgt.dao.impl; package io.entgra.device.mgt.core.device.mgt.core.otp.mgt.dao.impl;
import io.entgra.device.mgt.core.device.mgt.common.DeviceManagementConstants;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.DBConnectionException; import io.entgra.device.mgt.core.device.mgt.common.exceptions.DBConnectionException;
@ -56,7 +57,8 @@ public class GenericOTPManagementDAOImpl extends AbstractDAOImpl implements OTPM
+ "META_INFO, " + "META_INFO, "
+ "CREATED_AT," + "CREATED_AT,"
+ "TENANT_ID," + "TENANT_ID,"
+ "USERNAME) VALUES (?, ?, ?, ?, ?, ?, ?)"; + "USERNAME, "
+ "EXPIRY_TIME) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
try { try {
Connection conn = this.getDBConnection(); Connection conn = this.getDBConnection();
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
@ -70,6 +72,8 @@ public class GenericOTPManagementDAOImpl extends AbstractDAOImpl implements OTPM
stmt.setTimestamp(5, timestamp); stmt.setTimestamp(5, timestamp);
stmt.setInt(6, oneTimePinDTO.getTenantId()); stmt.setInt(6, oneTimePinDTO.getTenantId());
stmt.setString(7, oneTimePinDTO.getUsername()); stmt.setString(7, oneTimePinDTO.getUsername());
stmt.setInt(8, oneTimePinDTO.getExpiryTime() == 0
? DeviceManagementConstants.OTPProperties.OTP_DEFAULT_EXPIRY_SECONDS : oneTimePinDTO.getExpiryTime());
stmt.addBatch(); stmt.addBatch();
} }
stmt.executeBatch(); stmt.executeBatch();

@ -127,7 +127,8 @@ public class OTPManagementServiceImpl implements OTPManagementService {
} }
@Override @Override
public OneTimePinDTO isValidOTP(String oneTimeToken) throws OTPManagementException, BadRequestException { public OneTimePinDTO isValidOTP(String oneTimeToken, boolean requireRenewal) throws OTPManagementException,
BadRequestException {
if (StringUtils.isBlank(oneTimeToken)){ if (StringUtils.isBlank(oneTimeToken)){
String msg = "Received blank OTP to verify. OTP: " + oneTimeToken; String msg = "Received blank OTP to verify. OTP: " + oneTimeToken;
log.error(msg); log.error(msg);
@ -151,17 +152,19 @@ public class OTPManagementServiceImpl implements OTPManagementService {
oneTimePinDTO.getCreatedAt().getTime() + oneTimePinDTO.getExpiryTime() * 1000L); oneTimePinDTO.getCreatedAt().getTime() + oneTimePinDTO.getExpiryTime() * 1000L);
if (currentTimestamp.after(expiredTimestamp)) { if (currentTimestamp.after(expiredTimestamp)) {
String renewedOTP = UUID.randomUUID().toString(); if (requireRenewal) {
renewOTP(oneTimePinDTO, renewedOTP); String renewedOTP = UUID.randomUUID().toString();
Gson gson = new Gson(); renewOTP(oneTimePinDTO, renewedOTP);
Tenant tenant = gson.fromJson(oneTimePinDTO.getMetaInfo(), Tenant.class); Gson gson = new Gson();
Tenant tenant = gson.fromJson(oneTimePinDTO.getMetaInfo(), Tenant.class);
Properties props = new Properties(); Properties props = new Properties();
props.setProperty("first-name", tenant.getAdminFirstName()); props.setProperty("first-name", tenant.getAdminFirstName());
props.setProperty("otp-token", renewedOTP); props.setProperty("otp-token", renewedOTP);
props.setProperty("email", oneTimePinDTO.getEmail()); props.setProperty("email", oneTimePinDTO.getEmail());
props.setProperty("type", oneTimePinDTO.getEmailType()); props.setProperty("type", oneTimePinDTO.getEmailType());
sendMail(props, oneTimePinDTO.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE); sendMail(props, oneTimePinDTO.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE);
}
return null; return null;
} }
return oneTimePinDTO; return oneTimePinDTO;
@ -244,8 +247,14 @@ public class OTPManagementServiceImpl implements OTPManagementService {
for (String username : deviceEnrollmentInvitation.getUsernames()) { for (String username : deviceEnrollmentInvitation.getUsernames()) {
String emailAddress = DeviceManagerUtil.getUserClaimValue( String emailAddress = DeviceManagerUtil.getUserClaimValue(
username, DeviceManagementConstants.User.CLAIM_EMAIL_ADDRESS); username, DeviceManagementConstants.User.CLAIM_EMAIL_ADDRESS);
oneTimePinDTO = generateOneTimePin(emailAddress, OTPEmailTypes.DEVICE_ENROLLMENT.toString(), username,
null, tenantId, false); OneTimePinDTO oneTimePinData = new OneTimePinDTO();
oneTimePinData.setEmail(emailAddress);
oneTimePinData.setTenantId(tenantId);
oneTimePinData.setUsername(username);
oneTimePinData.setEmailType(OTPEmailTypes.USER_INVITE.toString());
oneTimePinDTO = generateOneTimePin(oneTimePinData, false);
oneTimePinDTOList.add(oneTimePinDTO); oneTimePinDTOList.add(oneTimePinDTO);
props.setProperty("first-name", DeviceManagerUtil. props.setProperty("first-name", DeviceManagerUtil.
getUserClaimValue(username, DeviceManagementConstants.User.CLAIM_FIRST_NAME)); getUserClaimValue(username, DeviceManagementConstants.User.CLAIM_FIRST_NAME));
@ -279,27 +288,17 @@ public class OTPManagementServiceImpl implements OTPManagementService {
/** /**
* Create One Time Token * Create One Time Token
* @param email email * @param oneTimePinDTO Data related to the one time pin
* @param emailType email type
* @param userName username
* @param metaDataObj meta data object
* @param tenantId tenant Id
* @return {@link OneTimePinDTO} * @return {@link OneTimePinDTO}
*/ */
@Override @Override
public OneTimePinDTO generateOneTimePin(String email, String emailType, String userName, Object metaDataObj, public OneTimePinDTO generateOneTimePin(OneTimePinDTO oneTimePinDTO, boolean persistPin) throws OTPManagementException {
int tenantId, boolean persistPin) throws OTPManagementException {
String otpValue = UUID.randomUUID().toString(); String otpValue = UUID.randomUUID().toString();
Gson gson = new Gson(); Gson gson = new Gson();
String metaInfo = gson.toJson(metaDataObj); String metaInfo = gson.toJson(oneTimePinDTO.getMetaInfo());
OneTimePinDTO oneTimePinDTO = new OneTimePinDTO();
oneTimePinDTO.setEmail(email);
oneTimePinDTO.setTenantId(tenantId);
oneTimePinDTO.setUsername(userName);
oneTimePinDTO.setEmailType(emailType);
oneTimePinDTO.setMetaInfo(metaInfo); oneTimePinDTO.setMetaInfo(metaInfo);
oneTimePinDTO.setOtpToken(otpValue); oneTimePinDTO.setOtpToken(otpValue);

@ -19,7 +19,6 @@
package io.entgra.device.mgt.core.ui.request.interceptor; package io.entgra.device.mgt.core.ui.request.interceptor;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.AuthData; import io.entgra.device.mgt.core.ui.request.interceptor.beans.AuthData;
@ -28,12 +27,17 @@ import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse; import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import io.entgra.device.mgt.core.device.mgt.common.DeviceManagementConstants;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.OTPManagementException;
import io.entgra.device.mgt.core.device.mgt.common.otp.mgt.OTPEmailTypes;
import io.entgra.device.mgt.core.device.mgt.common.otp.mgt.dto.OneTimePinDTO;
import io.entgra.device.mgt.core.device.mgt.common.spi.OTPManagementService;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
@ -55,71 +59,28 @@ public class DefaultTokenHandler extends HttpServlet {
HttpSession httpSession = req.getSession(false); HttpSession httpSession = req.getSession(false);
if (httpSession != null) { if (httpSession != null) {
AuthData authData = (AuthData) httpSession.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); String userWithDomain = (String) httpSession.getAttribute(HandlerConstants.USERNAME_WITH_DOMAIN);
if (authData == null) { String[] userNameParts = userWithDomain.split("@");
HandlerUtil.sendUnAuthorizeResponse(resp);
return; OneTimePinDTO oneTimePinData = new OneTimePinDTO();
oneTimePinData.setEmail(OTPEmailTypes.REMOTE_SESSION.toString());
oneTimePinData.setEmailType(OTPEmailTypes.REMOTE_SESSION.toString());
oneTimePinData.setUsername(userNameParts[0]);
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
RealmService realmService = (RealmService) ctx.getOSGiService(RealmService.class, null);
try {
oneTimePinData.setTenantId(realmService.getTenantManager().getTenantId(userNameParts[1]));
} catch (UserStoreException e) {
throw new RuntimeException(e);
} }
oneTimePinData.setExpiryTime(DeviceManagementConstants.OTPProperties.OTP_DEFAULT_EXPIRY_SECONDS);
AuthData defaultAuthData = (AuthData) httpSession OTPManagementService otpManagementService = HandlerUtil.getOTPManagementService();
.getAttribute(HandlerConstants.SESSION_DEFAULT_AUTH_DATA_KEY); try {
if (defaultAuthData != null) { oneTimePinData = otpManagementService.generateOneTimePin(oneTimePinData, true);
HandlerUtil.handleSuccess(resp, constructSuccessProxyResponse(defaultAuthData.getAccessToken())); HandlerUtil.handleSuccess(resp, constructSuccessProxyResponse(oneTimePinData.getOtpToken()));
return; } catch (OTPManagementException e) {
} log.error("Failed while generating remote session OTP for user " + userWithDomain, e);
HandlerUtil.handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR);
String clientId = authData.getClientId();
String clientSecret = authData.getClientSecret();
String queryString = req.getQueryString();
String scopeString = "";
if (StringUtils.isNotEmpty(queryString)) {
scopeString = req.getParameter("scopes");
if (scopeString != null) {
scopeString = "?scopes=" + scopeString;
}
}
String iotsCoreUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme());
String tokenUrl = iotsCoreUrl + "/api/device-mgt/v1.0/devices/" + clientId
+ "/" + clientSecret + "/default-token" + scopeString;
HttpGet defaultTokenRequest = new HttpGet(tokenUrl);
defaultTokenRequest
.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken());
defaultTokenRequest
.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString());
ProxyResponse tokenResultResponse = HandlerUtil.execute(defaultTokenRequest);
if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) {
log.error("Error occurred while invoking the API to get default token data.");
HandlerUtil.handleError(resp, tokenResultResponse);
return;
}
String tokenResult = tokenResultResponse.getData();
if (tokenResult == null) {
log.error("Invalid default token response is received.");
HandlerUtil.handleError(resp, tokenResultResponse);
return;
}
JsonParser jsonParser = new JsonParser();
JsonElement jTokenResult = jsonParser.parse(tokenResult);
if (jTokenResult.isJsonObject()) {
JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject();
AuthData newDefaultAuthData = new AuthData();
newDefaultAuthData.setClientId(clientId);
newDefaultAuthData.setClientSecret(clientSecret);
String defaultToken = jTokenResultAsJsonObject.get("accessToken").getAsString();
newDefaultAuthData.setAccessToken(defaultToken);
newDefaultAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refreshToken").getAsString());
newDefaultAuthData.setScope(jTokenResultAsJsonObject.get("scopes").getAsString());
httpSession.setAttribute(HandlerConstants.SESSION_DEFAULT_AUTH_DATA_KEY, newDefaultAuthData);
HandlerUtil.handleSuccess(resp, constructSuccessProxyResponse(defaultToken));
} }
} else { } else {
HandlerUtil.sendUnAuthorizeResponse(resp); HandlerUtil.sendUnAuthorizeResponse(resp);

@ -120,6 +120,7 @@ public class UserHandler extends HttpServlet {
proxyResponse.setData( proxyResponse.setData(
jTokenResultAsJsonObject.get("username").getAsString().replaceAll("@carbon.super", "")); jTokenResultAsJsonObject.get("username").getAsString().replaceAll("@carbon.super", ""));
HandlerUtil.handleSuccess(resp, proxyResponse); HandlerUtil.handleSuccess(resp, proxyResponse);
httpSession.setAttribute(HandlerConstants.USERNAME_WITH_DOMAIN, jTokenResultAsJsonObject.get("username").getAsString());
log.info("Customer login", userLogContextBuilder.setUserName(proxyResponse.getData()).setUserRegistered(true).build()); log.info("Customer login", userLogContextBuilder.setUserName(proxyResponse.getData()).setUserRegistered(true).build());
} }
} catch (IOException e) { } catch (IOException e) {

@ -106,4 +106,5 @@ public class HandlerConstants {
public static final String IOT_REPORTING_WEBAPP_HOST_ENV_VAR = "iot.reporting.webapp.host"; public static final String IOT_REPORTING_WEBAPP_HOST_ENV_VAR = "iot.reporting.webapp.host";
public static final String USER_SCOPES = "userScopes"; public static final String USER_SCOPES = "userScopes";
public static final String HUBSPOT_CHAT_URL = "api.hubapi.com"; public static final String HUBSPOT_CHAT_URL = "api.hubapi.com";
public static final String USERNAME_WITH_DOMAIN = "usernameWithDomain";
} }

@ -55,6 +55,8 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse; import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import io.entgra.device.mgt.core.device.mgt.common.spi.OTPManagementService;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -79,6 +81,8 @@ public class HandlerUtil {
private static boolean isLoginCacheInitialized = false; private static boolean isLoginCacheInitialized = false;
private static AuthData authData; private static AuthData authData;
private static OTPManagementService otpManagementService;
/*** /***
* *
* @param httpRequest - httpMethod e.g:- HttpPost, HttpGet * @param httpRequest - httpMethod e.g:- HttpPost, HttpGet
@ -751,4 +755,12 @@ public class HandlerUtil {
public static boolean isPropertyDefined(String property) { public static boolean isPropertyDefined(String property) {
return StringUtils.isEmpty(System.getProperty(property)); return StringUtils.isEmpty(System.getProperty(property));
} }
public static OTPManagementService getOTPManagementService() {
if (otpManagementService == null) {
otpManagementService = (OTPManagementService) PrivilegedCarbonContext
.getThreadLocalCarbonContext().getOSGiService(OTPManagementService.class, null);
}
return otpManagementService;
}
} }

@ -35,6 +35,7 @@ import io.entgra.device.mgt.core.webapp.authenticator.framework.Utils.Utils;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Base64; import java.util.Base64;
import java.util.Properties; import java.util.Properties;
import java.util.StringTokenizer;
public class BasicAuthAuthenticator implements WebappAuthenticator { public class BasicAuthAuthenticator implements WebappAuthenticator {
@ -50,15 +51,23 @@ public class BasicAuthAuthenticator implements WebappAuthenticator {
@Override @Override
public boolean canHandle(Request request) { public boolean canHandle(Request request) {
/* /*
This is done to avoid every endpoint being able to use basic auth. Add the following to This is done to avoid every web app being able to use basic auth. Add the following to
the required web.xml of the web app. the required web.xml of the web app. This is a global config for a web app to allow all
contexts of the web app to use basic auth
<context-param> <context-param>
<param-name>basicAuth</param-name> <param-name>basicAuth</param-name>
<param-value>true</param-value> <param-value>true</param-value>
</context-param> </context-param>
Adding the basicAuthAllowList parameter allows to selectively allow some context paths in a
web app to use basic auth while all the other context remain unavailable with basic auth.
If this parameter is present, any context that requires basic auth must be specially
added as comma separated list to the param-value of basicAuthAllowList.
*/ */
if (!isAuthenticationSupported(request)) { if (!isAllowListedForBasicAuth(request)) {
return false; if (!isAuthenticationSupported(request)) {
return false;
}
} }
if (request.getCoyoteRequest() == null || request.getCoyoteRequest().getMimeHeaders() == null) { if (request.getCoyoteRequest() == null || request.getCoyoteRequest().getMimeHeaders() == null) {
return false; return false;
@ -75,6 +84,20 @@ public class BasicAuthAuthenticator implements WebappAuthenticator {
return false; return false;
} }
private boolean isAllowListedForBasicAuth(Request request) {
String param = request.getContext().findParameter("basicAuthAllowList");
if (param != null && !param.isEmpty()) {
//Add the nonSecured end-points to cache
String[] basicAuthAllowList = param.split(",");
for (String contexPath : basicAuthAllowList) {
if (request.getRequestURI().toString().endsWith(contexPath.trim())) {
return true;
}
}
}
return false;
}
@Override @Override
public AuthenticationInfo authenticate(Request request, Response response) { public AuthenticationInfo authenticate(Request request, Response response) {
AuthenticationInfo authenticationInfo = new AuthenticationInfo(); AuthenticationInfo authenticationInfo = new AuthenticationInfo();

@ -74,9 +74,17 @@ public class CertificateAuthenticator implements WebappAuthenticator {
// When there is a load balancer terminating mutual SSL, it should pass this header along and // When there is a load balancer terminating mutual SSL, it should pass this header along and
// as the value of this header, the client certificate subject dn should be passed. // as the value of this header, the client certificate subject dn should be passed.
if (request.getHeader(PROXY_MUTUAL_AUTH_HEADER) != null) { if (request.getHeader(PROXY_MUTUAL_AUTH_HEADER) != null) {
if (log.isDebugEnabled()) {
log.debug("PROXY_MUTUAL_AUTH_HEADER " + request.getHeader(PROXY_MUTUAL_AUTH_HEADER));
}
CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance(). CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance().
getCertificateManagementService().verifySubjectDN(request.getHeader(PROXY_MUTUAL_AUTH_HEADER)); getCertificateManagementService().verifySubjectDN(request.getHeader(PROXY_MUTUAL_AUTH_HEADER));
authenticationInfo = checkCertificateResponse(certificateResponse); authenticationInfo = checkCertificateResponse(certificateResponse);
if (log.isDebugEnabled()) {
log.debug("Certificate Serial : " + certificateResponse.getSerialNumber()
+ ", CN : " + certificateResponse.getCommonName()
+ " , username" + authenticationInfo.getUsername());
}
} }
else if (request.getHeader(MUTUAL_AUTH_HEADER) != null) { else if (request.getHeader(MUTUAL_AUTH_HEADER) != null) {
Object object = request.getAttribute(CLIENT_CERTIFICATE_ATTRIBUTE); Object object = request.getAttribute(CLIENT_CERTIFICATE_ATTRIBUTE);
@ -89,7 +97,6 @@ public class CertificateAuthenticator implements WebappAuthenticator {
CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance(). CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance().
getCertificateManagementService().verifyPEMSignature(clientCertificate[0]); getCertificateManagementService().verifyPEMSignature(clientCertificate[0]);
authenticationInfo = checkCertificateResponse(certificateResponse); authenticationInfo = checkCertificateResponse(certificateResponse);
} else { } else {
authenticationInfo.setStatus(Status.FAILURE); authenticationInfo.setStatus(Status.FAILURE);
authenticationInfo.setMessage("No client certificate is present"); authenticationInfo.setMessage("No client certificate is present");

@ -49,8 +49,18 @@ public class OneTimeTokenAuthenticator implements WebappAuthenticator {
try { try {
OTPManagementService otpManagementService = AuthenticatorFrameworkDataHolder.getInstance() OTPManagementService otpManagementService = AuthenticatorFrameworkDataHolder.getInstance()
.getOtpManagementService(); .getOtpManagementService();
OneTimePinDTO validOTP = otpManagementService.isValidOTP(request.getHeader(Constants.HTTPHeaders OneTimePinDTO validOTP;
.ONE_TIME_TOKEN_HEADER)); if (request.getRequestURI().toString().endsWith("cloud/download-url")
|| request.getRequestURI().toString().endsWith("cloud/tenant")) {
validOTP = otpManagementService.isValidOTP(request.getHeader(Constants.HTTPHeaders
.ONE_TIME_TOKEN_HEADER), true);
} else {
log.info("Validating OTP for enrollments PIN: " + request.getHeader(Constants
.HTTPHeaders.ONE_TIME_TOKEN_HEADER));
validOTP = otpManagementService.isValidOTP(request.getHeader(Constants.HTTPHeaders
.ONE_TIME_TOKEN_HEADER), false);
}
if (validOTP != null) { if (validOTP != null) {
authenticationInfo.setStatus(Status.CONTINUE); authenticationInfo.setStatus(Status.CONTINUE);
authenticationInfo.setTenantId(validOTP.getTenantId()); authenticationInfo.setTenantId(validOTP.getTenantId());

Loading…
Cancel
Save