diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java index 15198d210f..6d1156986d 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java @@ -100,7 +100,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { } @Override - public ProvisionResponse provisionDeviceViaX509Chain(DeviceProfile targetProfile, ProvisionRequest provisionRequest) { + public ProvisionResponse provisionDeviceViaX509Chain(DeviceProfile targetProfile, ProvisionRequest provisionRequest) throws ProvisionFailedException { if (targetProfile == null) { throw new ProvisionFailedException("Device profile is not specified!"); } @@ -110,9 +110,10 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { X509CertificateChainProvisionConfiguration configuration = (X509CertificateChainProvisionConfiguration) targetProfile.getProfileData().getProvisionConfiguration(); String certificateValue = provisionRequest.getCredentialsData().getX509CertHash(); String certificateRegEx = configuration.getCertificateRegExPattern(); - String deviceName = extractDeviceNameFromCertificateCNByRegEx(targetProfile, certificateValue, certificateRegEx); + String commonName = getCNFromX509Certificate(certificateValue); + String deviceName = extractDeviceNameFromCNByRegEx(targetProfile, commonName, certificateRegEx); if (StringUtils.isBlank(deviceName)) { - log.warn("Device name cannot be extracted using regex [{}] for certificate [{}]", certificateRegEx, certificateValue); + log.warn("[{}][{}] Failed to extract device name using [{}] and certificate: [{}]", targetProfile.getTenantId(), targetProfile.getId(), certificateRegEx, certificateValue); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); } provisionRequest.setDeviceName(deviceName); @@ -120,7 +121,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { X509CertificateChainProvisionConfiguration x509Configuration = (X509CertificateChainProvisionConfiguration) targetProfile.getProfileData().getProvisionConfiguration(); if (targetDevice != null && targetDevice.getDeviceProfileId().equals(targetProfile.getId())) { DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(targetDevice.getTenantId(), targetDevice.getId()); - if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) { + if (DeviceCredentialsType.X509_CERTIFICATE.equals(deviceCredentials.getCredentialsType())) { String updatedDeviceCertificateValue = provisionRequest.getCredentialsData().getX509CertHash(); deviceCredentials = updateDeviceCredentials(targetDevice.getTenantId(), deviceCredentials, updatedDeviceCertificateValue, DeviceCredentialsType.X509_CERTIFICATE); @@ -295,21 +296,25 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { auditLogService.logEntityAction(tenantId, customerId, new UserId(UserId.NULL_UUID), device.getName(), device.getId(), device, actionType, null, provisionRequest); } - private String extractDeviceNameFromCertificateCNByRegEx(DeviceProfile profile, String x509Value, String regex) { + private String getCNFromX509Certificate(String x509Value) { try { - String commonName = SslUtil.parseCommonName(SslUtil.readCertFile(x509Value)); - log.trace("Extract CN [{}] by regex pattern [{}]", commonName, regex); - Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(commonName); - if (matcher.find()) { - return matcher.group(1); - } else { - return null; - } - } catch (Exception ignored) { - log.trace("[{}][{}] Failed to extract device name using [{}] and certificate: [{}]", profile.getTenantId(), profile.getId(), regex, x509Value); + return SslUtil.parseCommonName(SslUtil.readCertFile(x509Value)); + } catch (Exception e) { return null; } } + public String extractDeviceNameFromCNByRegEx(DeviceProfile profile, String commonName, String regex) { + try { + log.trace("Extract device name from CN [{}] by regex pattern [{}]", commonName, regex); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(commonName); + if (matcher.find()) { + return matcher.group(1); + } + } catch (Exception ignored) {} + log.trace("[{}][{}] Failed to match device name using [{}] from CN: [{}]", profile.getTenantId(), profile.getId(), regex, commonName); + return null; + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index b99c9c2509..57aa1e735e 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.OtaPackage; @@ -113,7 +114,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static org.thingsboard.server.common.data.DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN; import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH; import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.VALID; @@ -246,7 +246,7 @@ public class DefaultTransportApiService implements TransportApiService { return getDeviceInfo(credentials); } DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByProvisionDeviceKey(certificateHash); - if (deviceProfile != null && X509_CERTIFICATE_CHAIN.equals(deviceProfile.getProvisionType())) { + if (deviceProfile != null && DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN.equals(deviceProfile.getProvisionType())) { String updatedDeviceProvisionSecret = chain.get(0); ProvisionRequest provisionRequest = createProvisionRequest(updatedDeviceProvisionSecret); try { @@ -259,7 +259,7 @@ public class DefaultTransportApiService implements TransportApiService { return getEmptyTransportApiResponseFuture(); } } else if (deviceProfile != null) { - log.warn("[{}] Device Profile provision configuration mismatched: expected {}, actual {}", deviceProfile.getId(), X509_CERTIFICATE_CHAIN, deviceProfile.getProvisionType()); + log.warn("[{}][{}] Device Profile provision configuration mismatched: expected {}, actual {}", deviceProfile.getTenantId(), deviceProfile.getId(), DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN, deviceProfile.getProvisionType()); } } return getEmptyTransportApiResponseFuture(); diff --git a/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java new file mode 100644 index 0000000000..0ccae57e0f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java @@ -0,0 +1,267 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.device.provision; + + +import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.device.profile.X509CertificateChainProvisionConfiguration; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.msg.EncryptionUtil; +import org.thingsboard.server.common.transport.util.SslUtil; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.audit.AuditLogService; +import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceProfileService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.device.provision.ProvisionFailedException; +import org.thingsboard.server.dao.device.provision.ProvisionRequest; +import org.thingsboard.server.dao.device.provision.ProvisionResponse; +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; +import org.thingsboard.server.service.device.DeviceProvisionServiceImpl;;import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = DeviceProvisionServiceImpl.class) +public class DeviceProvisionServiceTest { + + + @MockBean + protected TbQueueProducerProvider producerProvider; + @MockBean + protected TbQueueProducer> ruleEngineMsgProducer; + @MockBean + protected TbClusterService clusterService; + @MockBean + protected DeviceProfileService deviceProfileService; + @MockBean + protected DeviceService deviceService; + @MockBean + protected DeviceCredentialsService deviceCredentialsService; + @MockBean + protected AttributesService attributesService; + @MockBean + protected AuditLogService auditLogService; + @MockBean + protected PartitionService partitionService; + @SpyBean + DeviceProvisionServiceImpl service; + + private String[] chain; + + @Before + public void setUp() { + String filePath = "src/test/resources/provision/x509ChainProvisionTest.pem"; + try { + String certificateChain = Files.readString(Paths.get(filePath)); + certificateChain = certTrimNewLinesForChainInDeviceProfile(certificateChain); + chain = fetchLeafCertificateFromChain(certificateChain); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + @Test + public void provisionDeviceViaX509Certificate() { + var tenant = createTenant(); + var deviceProfile = createDeviceProfile(tenant.getId(), chain[1], true); + + var device = createDevice(tenant.getId(), deviceProfile.getId()); + when(deviceService.findDeviceByTenantIdAndName(any(), any())).thenReturn(device); + + var deviceCredentials = createDeviceCredentials(chain[0], device.getId()); + when(deviceCredentialsService.findDeviceCredentialsByDeviceId(any(), any())).thenReturn(deviceCredentials); + when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials); + + ProvisionResponse response = service.provisionDeviceViaX509Chain(deviceProfile, createProvisionRequest(chain[0])); + + verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any()); + verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any()); + verify(deviceCredentialsService, times(1)).updateDeviceCredentials(any(), any()); + + Assertions.assertThat(response.getResponseStatus()).isEqualTo(ProvisionResponseStatus.SUCCESS); + Assertions.assertThat(response.getDeviceCredentials()).isEqualTo(deviceCredentials); + } + + @Test + public void provisionDeviceWithIncorrectConfiguration() { + var tenant = createTenant(); + var deviceProfile = createDeviceProfile(tenant.getId(), chain[1], false); + + Assertions.assertThatThrownBy(() -> + service.provisionDeviceViaX509Chain(deviceProfile, createProvisionRequest(chain[0]))) + .isInstanceOf(ProvisionFailedException.class); + + verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any()); + } + + @Test + public void matchDeviceNameFromX509CNCertificateByRegex() { + var tenant = createTenant(); + var deviceProfile = createDeviceProfile(tenant.getId(), chain[1], true); + X509CertificateChainProvisionConfiguration configuration = (X509CertificateChainProvisionConfiguration) deviceProfile.getProfileData().getProvisionConfiguration(); + String CN = getCNFromX509Certificate(chain[0]); + String deviceName = service.extractDeviceNameFromCNByRegEx(deviceProfile, CN, configuration.getCertificateRegExPattern()); + + Assertions.assertThat(deviceName).isNotBlank(); + Assertions.assertThat(deviceName).isEqualTo("deviceCertificate"); + } + + @Test + public void matchDeviceNameFromCNByRegex() { + var CN = "DeviceA.company.com"; + var regex = "(.*)\\.company.com"; + var result = service.extractDeviceNameFromCNByRegEx(null, CN, regex); + Assertions.assertThat(result).isNotBlank(); + Assertions.assertThat(result).isEqualTo("DeviceA"); + + CN = "DeviceA@company.com"; + regex = "(.*)@company.com"; + result = service.extractDeviceNameFromCNByRegEx(null, CN, regex); + Assertions.assertThat(result).isNotBlank(); + Assertions.assertThat(result).isEqualTo("DeviceA"); + + CN = "prefixDeviceAsuffix@company.com"; + regex = "prefix(.*)suffix@company.com"; + result = service.extractDeviceNameFromCNByRegEx(null, CN, regex); + Assertions.assertThat(result).isNotBlank(); + Assertions.assertThat(result).isEqualTo("DeviceA"); + + CN = "prefixDeviceAsufix@company.com"; + regex = "prefix(.*)sufix@company.com"; + result = service.extractDeviceNameFromCNByRegEx(null, CN, regex); + Assertions.assertThat(result).isNotBlank(); + Assertions.assertThat(result).isEqualTo("DeviceA"); + + CN = "region.DeviceA.220423@company.com"; + regex = "\\D+\\.(.*)\\.\\d+@company.com"; + result = service.extractDeviceNameFromCNByRegEx(null, CN, regex); + Assertions.assertThat(result).isNotBlank(); + Assertions.assertThat(result).isEqualTo("DeviceA"); + } + + private DeviceProfile createDeviceProfile(TenantId tenantId, String certificateValue, boolean isAllowToCreateNewDevices) { + X509CertificateChainProvisionConfiguration provision = new X509CertificateChainProvisionConfiguration(); + provision.setProvisionDeviceSecret(certificateValue); + provision.setCertificateRegExPattern("([^@]+)"); + provision.setAllowCreateNewDevicesByX509Certificate(isAllowToCreateNewDevices); + + DeviceProfileData deviceProfileData = new DeviceProfileData(); + deviceProfileData.setProvisionConfiguration(provision); + + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setId(new DeviceProfileId(UUID.randomUUID())); + deviceProfile.setProfileData(deviceProfileData); + deviceProfile.setProvisionDeviceKey(EncryptionUtil.getSha3Hash(certificateValue)); + deviceProfile.setProvisionType(DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN); + deviceProfile.setTenantId(tenantId); + return deviceProfile; + } + + private Device createDevice(TenantId tenantId, DeviceProfileId deviceProfileId) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setId(new DeviceId(UUID.randomUUID())); + device.setDeviceProfileId(deviceProfileId); + device.setCustomerId(new CustomerId(UUID.randomUUID())); + return device; + } + + private Tenant createTenant() { + Tenant tenant = new Tenant(); + tenant.setId(new TenantId(UUID.randomUUID())); + return tenant; + } + + private DeviceCredentials createDeviceCredentials(String certificateValue, DeviceId deviceId) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(deviceId); + deviceCredentials.setCredentialsValue(certificateValue); + deviceCredentials.setCredentialsId(EncryptionUtil.getSha3Hash(certificateValue)); + deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); + return deviceCredentials; + } + + private ProvisionRequest createProvisionRequest(String certificateValue) { + return new ProvisionRequest(null, DeviceCredentialsType.X509_CERTIFICATE, + new ProvisionDeviceCredentialsData(null, null, null, null, certificateValue), + null); + } + + public static String certTrimNewLinesForChainInDeviceProfile(String input) { + return input.replaceAll("\n", "") + .replaceAll("\r", "") + .replaceAll("-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----\n") + .replaceAll("-----END CERTIFICATE-----", "\n-----END CERTIFICATE-----\n") + .trim(); + } + + private String[] fetchLeafCertificateFromChain(String value) { + List chain = new ArrayList<>(); + String regex = "-----BEGIN CERTIFICATE-----\\s*.*?\\s*-----END CERTIFICATE-----"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) { + chain.add(matcher.group(0)); + } + return chain.toArray(new String[0]); + } + + private String getCNFromX509Certificate(String x509Value) { + try { + return SslUtil.parseCommonName(SslUtil.readCertFile(x509Value)); + } catch (Exception e) { + return null; + } + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java b/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java index 21a03c6167..9687225bd1 100644 --- a/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java @@ -109,7 +109,8 @@ public class DefaultTransportApiServiceTest { @Before public void setUp() { - String filePath = "src/test/resources/mqtt/x509ChainProvisionTest.pem"; + + String filePath = "src/test/resources/provision/x509ChainProvisionTest.pem"; try { certificateChain = Files.readString(Paths.get(filePath)); certificateChain = certTrimNewLinesForChainInDeviceProfile(certificateChain); @@ -120,7 +121,7 @@ public class DefaultTransportApiServiceTest { } @Test - public void validateExistingDeviceX509Certificate() { + public void validateExistingDeviceByX509CertificateStrategy() { var device = createDevice(); when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device)); @@ -145,13 +146,13 @@ public class DefaultTransportApiServiceTest { when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials); var provisionResponse = createProvisionResponse(deviceCredentials); - when(deviceProvisionService.provisionDevice(any())).thenReturn(provisionResponse); + when(deviceProvisionService.provisionDeviceViaX509Chain(any(), any())).thenReturn(provisionResponse); service.validateOrCreateDeviceX509Certificate(certificateChain); verify(deviceProfileService, times(1)).findDeviceProfileByProvisionDeviceKey(any()); verify(deviceService, times(1)).findDeviceByIdAsync(any(), any()); verify(deviceCredentialsService, times(1)).findDeviceCredentialsByCredentialsId(any()); - verify(deviceProvisionService, times(1)).provisionDevice(any()); + verify(deviceProvisionService, times(1)).provisionDeviceViaX509Chain(any(), any()); } private DeviceProfile createDeviceProfile(String certificateValue) { diff --git a/application/src/test/resources/mqtt/x509ChainProvisionTest.pem b/application/src/test/resources/provision/x509ChainProvisionTest.pem similarity index 100% rename from application/src/test/resources/mqtt/x509ChainProvisionTest.pem rename to application/src/test/resources/provision/x509ChainProvisionTest.pem diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java index fa47ed0694..5fb6462c80 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProvisionService.java @@ -24,5 +24,5 @@ public interface DeviceProvisionService { ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) throws ProvisionFailedException; - ProvisionResponse provisionDeviceViaX509Chain(DeviceProfile deviceProfile, ProvisionRequest provisionRequest); + ProvisionResponse provisionDeviceViaX509Chain(DeviceProfile deviceProfile, ProvisionRequest provisionRequest) throws ProvisionFailedException; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java index d092c34861..24e5ceb1b2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java @@ -408,8 +408,8 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator.* - matches any character (until line terminators) -
**CN sample:** DeviceName\nAdditionalInfo -
**Pattern matches:** DeviceName +The regular expression is required to extract device name from the X509 certificate's common name. +The regular expression syntax is based on Java [Pattern](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html). +You may also use this [resource](https://regex101.com/) to test your expressions but make sure you select Java 8 flavor. -* **Pattern:** ^([^@]+) - matches any string that starts with one or more characters that are not the @ symbol (@ could be replaced by any other symbol) -
**CN sample:** DeviceName@AdditionalInfo -
**Pattern matches:** DeviceName +* **Pattern:**(.*)\.company.com- matches any characters before the ".company.com". +
**CN sample:**DeviceA.company.com +
**Result:**DeviceA -* **Pattern:** [\w]*$ (equivalent to [a-zA-Z0-9_]\*$) - matches zero or more occurences of any word character (letter, digit or underscore) at the end of the string -
**CN sample:** AdditionalInfo2110#DeviceName_01 -
**Pattern matches:** DeviceName_01 +* **Pattern:** (.*)@company.com- matches any characters before the "@company.com". +
**CN sample:**DeviceA@company.com +
**Result:**DeviceA -**Note:** Client will get error response in case regex is failed to match. +* **Pattern:** prefix(.*)suffix@company.com- matches characters between "prefix" and "suffix@company.com". +
**CN sample:**prefixDeviceAsuffix@company.com +
**Pattern matches:** DeviceA + +* **Pattern:** \\D+\\.(.*)\\.\\d+@company.com- matches characters between not digits prefix followed by period and sequence of digits with "@company.com" ending. +
**CN sample:**region.DeviceA.220423@company.com +
**Pattern matches:** DeviceA