mirror of
https://repository.entgra.net/community/device-mgt-core.git
synced 2025-10-06 02:01:45 +00:00
OTP for enrollment with Mutual TLS
Fixes https://roadmap.entgra.net/issues/10093
This commit is contained in:
parent
1e252dd67f
commit
a603a69f3e
@ -134,6 +134,8 @@ public final class DeviceManagementConstants {
|
||||
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";
|
||||
|
||||
public static final int OTP_DEFAULT_EXPIRY_SECONDS = 3600;
|
||||
}
|
||||
|
||||
public static final class EventServices {
|
||||
|
||||
@ -33,6 +33,7 @@ public class QREnrollmentDetails {
|
||||
public void setCustomValues(Map<String, String> customValues) {
|
||||
this.customValues = customValues;
|
||||
}
|
||||
int tokenExpiry;
|
||||
|
||||
public String getOwnershipType() { return ownershipType; }
|
||||
|
||||
@ -45,4 +46,12 @@ public class QREnrollmentDetails {
|
||||
public String getEnrollmentMode() { return 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;
|
||||
|
||||
public enum OTPEmailTypes {
|
||||
USER_VERIFY, DEVICE_ENROLLMENT
|
||||
USER_VERIFY, DEVICE_ENROLLMENT, USER_INVITE
|
||||
}
|
||||
|
||||
@ -35,7 +35,8 @@ public interface OTPManagementService {
|
||||
* @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;
|
||||
OneTimePinDTO isValidOTP(String oneTimeToken, boolean requireRenewal) throws
|
||||
OTPManagementException, BadRequestException;
|
||||
|
||||
/**
|
||||
* Invalidate the OTP and send welcome mail
|
||||
@ -59,8 +60,7 @@ public interface OTPManagementService {
|
||||
boolean hasEmailRegistered(String email, String emailDomain) throws OTPManagementException,
|
||||
DeviceManagementException;
|
||||
|
||||
OneTimePinDTO generateOneTimePin(String email, String emailType, String userName, Object metaDataObj,
|
||||
int tenantId, boolean persistPin) throws OTPManagementException;
|
||||
OneTimePinDTO generateOneTimePin(OneTimePinDTO oneTimePinData, boolean persistPin) throws OTPManagementException;
|
||||
|
||||
OneTimePinDTO getRenewedOtpByEmailAndMailType(String email, String emailType) throws OTPManagementException;
|
||||
|
||||
|
||||
@ -25,7 +25,12 @@ import io.entgra.device.mgt.core.device.mgt.core.otp.mgt.dao.OTPManagementDAO;
|
||||
import io.entgra.device.mgt.core.device.mgt.core.otp.mgt.exception.OTPManagementDAOException;
|
||||
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.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.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
@ -51,7 +56,8 @@ public class GenericOTPManagementDAOImpl extends AbstractDAOImpl implements OTPM
|
||||
+ "META_INFO, "
|
||||
+ "CREATED_AT,"
|
||||
+ "TENANT_ID,"
|
||||
+ "USERNAME) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
+ "USERNAME, "
|
||||
+ "EXPIRY_TIME) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
try {
|
||||
Connection conn = this.getDBConnection();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
@ -65,6 +71,8 @@ public class GenericOTPManagementDAOImpl extends AbstractDAOImpl implements OTPM
|
||||
stmt.setTimestamp(5, timestamp);
|
||||
stmt.setInt(6, oneTimePinDTO.getTenantId());
|
||||
stmt.setString(7, oneTimePinDTO.getUsername());
|
||||
stmt.setInt(8, oneTimePinDTO.getExpiryTime() == 0
|
||||
? DeviceManagementConstants.OTPProperties.OTP_DEFAULT_EXPIRY_SECONDS : oneTimePinDTO.getExpiryTime());
|
||||
stmt.addBatch();
|
||||
}
|
||||
stmt.executeBatch();
|
||||
|
||||
@ -117,7 +117,8 @@ public class OTPManagementServiceImpl implements OTPManagementService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OneTimePinDTO isValidOTP(String oneTimeToken) throws OTPManagementException, BadRequestException {
|
||||
public OneTimePinDTO isValidOTP(String oneTimeToken, boolean requireRenewal) throws OTPManagementException,
|
||||
BadRequestException {
|
||||
if (StringUtils.isBlank(oneTimeToken)){
|
||||
String msg = "Received blank OTP to verify. OTP: " + oneTimeToken;
|
||||
log.error(msg);
|
||||
@ -141,6 +142,7 @@ public class OTPManagementServiceImpl implements OTPManagementService {
|
||||
oneTimePinDTO.getCreatedAt().getTime() + oneTimePinDTO.getExpiryTime() * 1000L);
|
||||
|
||||
if (currentTimestamp.after(expiredTimestamp)) {
|
||||
if (requireRenewal) {
|
||||
String renewedOTP = UUID.randomUUID().toString();
|
||||
renewOTP(oneTimePinDTO, renewedOTP);
|
||||
Gson gson = new Gson();
|
||||
@ -152,6 +154,7 @@ public class OTPManagementServiceImpl implements OTPManagementService {
|
||||
props.setProperty("email", oneTimePinDTO.getEmail());
|
||||
props.setProperty("type", oneTimePinDTO.getEmailType());
|
||||
sendMail(props, oneTimePinDTO.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return oneTimePinDTO;
|
||||
@ -234,8 +237,14 @@ public class OTPManagementServiceImpl implements OTPManagementService {
|
||||
for (String username : deviceEnrollmentInvitation.getUsernames()) {
|
||||
String emailAddress = DeviceManagerUtil.getUserClaimValue(
|
||||
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);
|
||||
props.setProperty("first-name", DeviceManagerUtil.
|
||||
getUserClaimValue(username, DeviceManagementConstants.User.CLAIM_FIRST_NAME));
|
||||
@ -269,27 +278,17 @@ public class OTPManagementServiceImpl implements OTPManagementService {
|
||||
|
||||
/**
|
||||
* Create One Time Token
|
||||
* @param email email
|
||||
* @param emailType email type
|
||||
* @param userName username
|
||||
* @param metaDataObj meta data object
|
||||
* @param tenantId tenant Id
|
||||
* @param oneTimePinDTO Data related to the one time pin
|
||||
* @return {@link OneTimePinDTO}
|
||||
*/
|
||||
@Override
|
||||
public OneTimePinDTO generateOneTimePin(String email, String emailType, String userName, Object metaDataObj,
|
||||
int tenantId, boolean persistPin) throws OTPManagementException {
|
||||
public OneTimePinDTO generateOneTimePin(OneTimePinDTO oneTimePinDTO, boolean persistPin) throws OTPManagementException {
|
||||
|
||||
String otpValue = UUID.randomUUID().toString();
|
||||
|
||||
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.setOtpToken(otpValue);
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Base64;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class BasicAuthAuthenticator implements WebappAuthenticator {
|
||||
|
||||
@ -50,16 +51,24 @@ public class BasicAuthAuthenticator implements WebappAuthenticator {
|
||||
@Override
|
||||
public boolean canHandle(Request request) {
|
||||
/*
|
||||
This is done to avoid every endpoint being able to use basic auth. Add the following to
|
||||
the required web.xml of the web app.
|
||||
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. This is a global config for a web app to allow all
|
||||
contexts of the web app to use basic auth
|
||||
<context-param>
|
||||
<param-name>basicAuth</param-name>
|
||||
<param-value>true</param-value>
|
||||
</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 (!isAllowListedForBasicAuth(request)) {
|
||||
if (!isAuthenticationSupported(request)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (request.getCoyoteRequest() == null || request.getCoyoteRequest().getMimeHeaders() == null) {
|
||||
return false;
|
||||
}
|
||||
@ -75,6 +84,20 @@ public class BasicAuthAuthenticator implements WebappAuthenticator {
|
||||
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
|
||||
public AuthenticationInfo authenticate(Request request, Response response) {
|
||||
AuthenticationInfo authenticationInfo = new AuthenticationInfo();
|
||||
|
||||
@ -74,21 +74,30 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
// 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.
|
||||
if (request.getHeader(PROXY_MUTUAL_AUTH_HEADER) != null) {
|
||||
log.info("PROXY_MUTUAL_AUTH_HEADER " + request.getHeader(PROXY_MUTUAL_AUTH_HEADER));
|
||||
CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance().
|
||||
getCertificateManagementService().verifySubjectDN(request.getHeader(PROXY_MUTUAL_AUTH_HEADER));
|
||||
log.info("clientCertificate" + certificateResponse.getSerialNumber());
|
||||
log.info("clientCertificate" + certificateResponse.getCommonName());
|
||||
authenticationInfo = checkCertificateResponse(certificateResponse);
|
||||
log.info("username" + authenticationInfo.getUsername());
|
||||
}
|
||||
else if (request.getHeader(MUTUAL_AUTH_HEADER) != null) {
|
||||
log.info("MUTUAL_AUTH_HEADER");
|
||||
Object object = request.getAttribute(CLIENT_CERTIFICATE_ATTRIBUTE);
|
||||
X509Certificate[] clientCertificate = null;
|
||||
if (object instanceof X509Certificate[]) {
|
||||
log.info("clientCertificate");
|
||||
clientCertificate = (X509Certificate[]) request.
|
||||
getAttribute(CLIENT_CERTIFICATE_ATTRIBUTE);
|
||||
}
|
||||
if (clientCertificate != null && clientCertificate[0] != null) {
|
||||
CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance().
|
||||
getCertificateManagementService().verifyPEMSignature(clientCertificate[0]);
|
||||
log.info("clientCertificate" + certificateResponse.getSerialNumber());
|
||||
log.info("clientCertificate" + certificateResponse.getCommonName());
|
||||
authenticationInfo = checkCertificateResponse(certificateResponse);
|
||||
log.info("username" + authenticationInfo.getUsername());
|
||||
|
||||
} else {
|
||||
authenticationInfo.setStatus(Status.FAILURE);
|
||||
|
||||
@ -49,8 +49,18 @@ public class OneTimeTokenAuthenticator implements WebappAuthenticator {
|
||||
try {
|
||||
OTPManagementService otpManagementService = AuthenticatorFrameworkDataHolder.getInstance()
|
||||
.getOtpManagementService();
|
||||
OneTimePinDTO validOTP = otpManagementService.isValidOTP(request.getHeader(Constants.HTTPHeaders
|
||||
.ONE_TIME_TOKEN_HEADER));
|
||||
OneTimePinDTO validOTP;
|
||||
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) {
|
||||
authenticationInfo.setStatus(Status.CONTINUE);
|
||||
authenticationInfo.setTenantId(validOTP.getTenantId());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user