diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java index cb97cf8892..fe1d829a82 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/CertificateDAO.java @@ -51,6 +51,16 @@ public interface CertificateDAO { */ 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. * diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/AbstractCertificateDAOImpl.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/AbstractCertificateDAOImpl.java index 4af136c987..e536eaf646 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/AbstractCertificateDAOImpl.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/dao/impl/AbstractCertificateDAOImpl.java @@ -119,6 +119,42 @@ public abstract class AbstractCertificateDAOImpl implements CertificateDAO{ 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 public List searchCertificate(String serialNumber) throws CertificateManagementDAOException { diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java index 2cb6d4098f..e3973ed152 100755 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/CertificateGenerator.java @@ -44,12 +44,17 @@ import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.util.Store; -import org.jscep.message.*; +import org.jscep.message.CertRep; +import org.jscep.message.MessageDecodingException; +import org.jscep.message.MessageEncodingException; +import org.jscep.message.PkcsPkiEnvelopeDecoder; +import org.jscep.message.PkcsPkiEnvelopeEncoder; +import org.jscep.message.PkiMessage; +import org.jscep.message.PkiMessageDecoder; +import org.jscep.message.PkiMessageEncoder; import org.jscep.transaction.FailInfo; import org.jscep.transaction.Nonce; import org.jscep.transaction.TransactionId; -import org.wso2.carbon.certificate.mgt.core.cache.CertificateCacheManager; -import org.wso2.carbon.certificate.mgt.core.cache.impl.CertificateCacheManagerImpl; import org.wso2.carbon.certificate.mgt.core.dao.CertificateDAO; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOException; import org.wso2.carbon.certificate.mgt.core.dao.CertificateManagementDAOFactory; @@ -72,13 +77,28 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; -import java.security.*; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; import java.security.cert.Certificate; -import java.security.cert.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; +import java.util.concurrent.TimeUnit; public class CertificateGenerator { @@ -338,15 +358,31 @@ public class CertificateGenerator { CertificateResponse lookUpCertificate = null; KeyStoreReader keyStoreReader = new KeyStoreReader(); if (distinguishedName != null && !distinguishedName.isEmpty()) { - if (distinguishedName.contains("/CN=")) { - String[] dnSplits = distinguishedName.split("/"); - for (String dnPart : dnSplits) { - if (dnPart.contains("CN=")) { - String commonNameExtracted = dnPart.replace("CN=", ""); - lookUpCertificate = keyStoreReader.getCertificateBySerial(commonNameExtracted); - break; + if (distinguishedName.contains("CN=")) { + String[] dnSplits = null; + if (distinguishedName.contains("/")) { + dnSplits = distinguishedName.split("/"); + } else if (distinguishedName.contains(",")) { + //some older versions of nginx will forward the client certificate subject dn separated with commas + 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_ 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 { LdapName ldapName; try { @@ -757,4 +793,95 @@ public class CertificateGenerator { return generateCertificateFromCSR(privateKeyCA, certificationRequest, certCA.getIssuerX500Principal().getName()); } -} + + public X509Certificate generateAlteredCertificateFromCSR(String csr) + throws KeystoreException { + byte[] byteArrayBst = DatatypeConverter.parseBase64Binary(csr); + PKCS10CertificationRequest certificationRequest; + KeyStoreReader keyStoreReader = new KeyStoreReader(); + PrivateKey privateKeyCA = keyStoreReader.getCAPrivateKey(); + X509Certificate certCA = (X509Certificate) keyStoreReader.getCACertificate(); + + X509Certificate issuedCert; + try { + certificationRequest = new PKCS10CertificationRequest(byteArrayBst); + JcaContentSignerBuilder csBuilder = + new JcaContentSignerBuilder(CertificateManagementConstants.SIGNING_ALGORITHM); + ContentSigner signer = csBuilder.build(privateKeyCA); + + BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); + + //Reversing the order of components of the subject DN due to Nginx not verifying the client certificate + //generated by Java using this subject DN. + //Ref: https://stackoverflow.com/questions/33769978 & engineering mail SCEP implementation for Android + String[] dnParts = certCA.getSubjectDN().getName().split(","); + StringJoiner joiner = new StringJoiner(","); + for (int i = (dnParts.length - 1); i >= 0; i--) { + joiner.add(dnParts[i]); + } + String subjectDn = joiner.toString(); + X500Name issuerName = new X500Name(subjectDn); + String commonName = certificationRequest.getSubject().getRDNs(BCStyle.CN)[0].getFirst() + .getValue().toString(); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + X500Name subjectName = new X500Name("O=" + commonName + ",CN=" + + serialNumber + ", OU=tenant_"+tenantId); + Date startDate = new Date(System.currentTimeMillis()); + Date endDate = new Date(System.currentTimeMillis() + + TimeUnit.DAYS.toMillis(365 * 100)); + PublicKey publicKey = getPublicKeyFromRequest(certificationRequest); + + X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( + issuerName, serialNumber, startDate, endDate, + subjectName, publicKey); + + X509CertificateHolder certHolder = certBuilder.build(signer); + + CertificateFactory certificateFactory = CertificateFactory.getInstance + (CertificateManagementConstants.X_509); + byte[] encodedCertificate = certHolder.getEncoded(); + issuedCert = (X509Certificate) certificateFactory + .generateCertificate(new ByteArrayInputStream(encodedCertificate)); + + org.wso2.carbon.certificate.mgt.core.bean.Certificate certificate = + new org.wso2.carbon.certificate.mgt.core.bean.Certificate(); + List certificates = new ArrayList<>(); + certificate.setTenantId(PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId()); + certificate.setCertificate(issuedCert); + certificates.add(certificate); + saveCertInKeyStore(certificates); + + } catch (OperatorCreationException e) { + String errorMsg = "Error creating the content signer"; + log.error(errorMsg); + throw new KeystoreException(errorMsg, e); + } catch (CertificateException e) { + String errorMsg = "Error when opening the newly created certificate"; + log.error(errorMsg); + throw new KeystoreException(errorMsg, e); + } catch (InvalidKeySpecException e) { + String errorMsg = "Public key is having invalid specification"; + log.error(errorMsg); + throw new KeystoreException(errorMsg, e); + } catch (NoSuchAlgorithmException e) { + String errorMsg = "Could not find RSA algorithm"; + log.error(errorMsg); + throw new KeystoreException(errorMsg, e); + } catch (IOException e) { + String errorMsg = "Error while reading the csr"; + log.error(errorMsg); + throw new KeystoreException(errorMsg, e); + } + return issuedCert; + } + + private static PublicKey getPublicKeyFromRequest(PKCS10CertificationRequest request) + throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { + byte[] publicKeyBytes = request.getSubjectPublicKeyInfo().getEncoded(); + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); + return publicKey; + } + +} \ No newline at end of file diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java index 60a7800863..921f11e794 100755 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/impl/KeyStoreReader.java @@ -275,6 +275,43 @@ public class KeyStoreReader { 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 { CertificateResponse certificateResponse = null; try { diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java index becd68720b..393dbdd0ed 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementService.java @@ -79,4 +79,6 @@ public interface CertificateManagementService { List searchCertificates(String serialNumber) throws CertificateManagementException; + X509Certificate generateAlteredCertificateFromCSR(String csr) throws KeystoreException; + } diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java index c47472f35c..67cca297c4 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/service/CertificateManagementServiceImpl.java @@ -234,4 +234,9 @@ public class CertificateManagementServiceImpl implements CertificateManagementSe } } + @Override + public X509Certificate generateAlteredCertificateFromCSR(String csr) throws KeystoreException{ + return certificateGenerator.generateAlteredCertificateFromCSR(csr); + } + } diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/CertificateManagementConstants.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/CertificateManagementConstants.java index 5e5f02c7f0..96c6cc2148 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/CertificateManagementConstants.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/src/main/java/org/wso2/carbon/certificate/mgt/core/util/CertificateManagementConstants.java @@ -39,6 +39,7 @@ public final class CertificateManagementConstants { public static final String RSA_PRIVATE_KEY_END_TEXT = "-----END RSA PRIVATE KEY-----"; public static final String EMPTY_TEXT = ""; public static final int RSA_KEY_LENGTH = 2048; + public static final String SIGNING_ALGORITHM = "SHA256withRSA"; public static final class DataBaseTypes { private DataBaseTypes() { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java index 154594678c..f95a78ed84 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java @@ -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 { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/general/QREnrollmentDetails.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/general/QREnrollmentDetails.java index 150fddef09..37e696c206 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/general/QREnrollmentDetails.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/general/QREnrollmentDetails.java @@ -22,6 +22,7 @@ public class QREnrollmentDetails { String ownershipType; String username; String enrollmentMode; + int tokenExpiry; public String getOwnershipType() { return ownershipType; } @@ -34,4 +35,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; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/otp/mgt/OTPEmailTypes.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/otp/mgt/OTPEmailTypes.java index 72bbea982e..9a182a0b14 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/otp/mgt/OTPEmailTypes.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/otp/mgt/OTPEmailTypes.java @@ -18,5 +18,5 @@ package org.wso2.carbon.device.mgt.common.otp.mgt; public enum OTPEmailTypes { - USER_VERIFY, DEVICE_ENROLLMENT + USER_VERIFY, DEVICE_ENROLLMENT, USER_INVITE } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/spi/OTPManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/spi/OTPManagementService.java index bf1d112875..ed548499bf 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/spi/OTPManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/spi/OTPManagementService.java @@ -34,7 +34,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 @@ -58,8 +59,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; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/dao/impl/GenericOTPManagementDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/dao/impl/GenericOTPManagementDAOImpl.java index 574d6e7904..becea82ede 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/dao/impl/GenericOTPManagementDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/dao/impl/GenericOTPManagementDAOImpl.java @@ -19,6 +19,7 @@ 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.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; @@ -55,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(); @@ -69,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(); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/service/OTPManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/service/OTPManagementServiceImpl.java index 4c8161e100..2d90dc5727 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/service/OTPManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/otp/mgt/service/OTPManagementServiceImpl.java @@ -126,7 +126,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); @@ -150,17 +151,19 @@ public class OTPManagementServiceImpl implements OTPManagementService { oneTimePinDTO.getCreatedAt().getTime() + oneTimePinDTO.getExpiryTime() * 1000L); if (currentTimestamp.after(expiredTimestamp)) { - String renewedOTP = UUID.randomUUID().toString(); - renewOTP(oneTimePinDTO, renewedOTP); - Gson gson = new Gson(); - Tenant tenant = gson.fromJson(oneTimePinDTO.getMetaInfo(), Tenant.class); + if (requireRenewal) { + 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); - props.setProperty("email", oneTimePinDTO.getEmail()); - props.setProperty("type", oneTimePinDTO.getEmailType()); - sendMail(props, oneTimePinDTO.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE); + Properties props = new Properties(); + props.setProperty("first-name", tenant.getAdminFirstName()); + props.setProperty("otp-token", renewedOTP); + props.setProperty("email", oneTimePinDTO.getEmail()); + props.setProperty("type", oneTimePinDTO.getEmailType()); + sendMail(props, oneTimePinDTO.getEmail(), DeviceManagementConstants.EmailAttributes.USER_VERIFY_TEMPLATE); + } return null; } return oneTimePinDTO; @@ -243,8 +246,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)); @@ -278,27 +287,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); diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java index 4bd7779dda..f1b0339994 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java @@ -36,6 +36,7 @@ import org.wso2.carbon.webapp.authenticator.framework.Utils.Utils; import java.nio.charset.Charset; import java.util.Base64; import java.util.Properties; +import java.util.StringTokenizer; public class BasicAuthAuthenticator implements WebappAuthenticator { @@ -51,15 +52,23 @@ 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 basicAuth true + + 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)) { - return false; + if (!isAllowListedForBasicAuth(request)) { + if (!isAuthenticationSupported(request)) { + return false; + } } if (request.getCoyoteRequest() == null || request.getCoyoteRequest().getMimeHeaders() == null) { return false; @@ -76,6 +85,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(); diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java index 4bead3ad4f..6bccefe7ec 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/CertificateAuthenticator.java @@ -75,21 +75,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); diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/OneTimeTokenAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/OneTimeTokenAuthenticator.java index 9d290c51da..539e8be13d 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/OneTimeTokenAuthenticator.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/OneTimeTokenAuthenticator.java @@ -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());