sparkplug: validate namespace

This commit is contained in:
nickAS21 2023-03-02 13:19:55 +02:00
parent 6b121514fc
commit 0587933d5e
10 changed files with 46 additions and 30 deletions

View File

@ -57,9 +57,7 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilde
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
@ -93,7 +91,6 @@ import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.service.mail.TestMailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;

View File

@ -67,6 +67,7 @@ import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataTyp
import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataType.UInt64;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataType.UInt8;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMetricUtil.createMetric;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopicUtil.NAMESPACE;
/**
* Created by nickAS21 on 12.01.23
@ -79,7 +80,6 @@ public abstract class AbstractMqttV5ClientSparkplugTest extends AbstractMqttInte
protected Calendar calendar = Calendar.getInstance();
protected ThreadLocalRandom random = ThreadLocalRandom.current();
protected static final String NAMESPACE = "spBv1.0";
protected static final String groupId = "SparkplugBGroupId";
protected static final String edgeNode = "SparkpluBNode";
protected static final String keysBdSeq = "bdSeq";
@ -114,6 +114,14 @@ public abstract class AbstractMqttV5ClientSparkplugTest extends AbstractMqttInte
}
public void clientWithCorrectNodeAccessTokenWithNDEATH(long ts, long value) throws Exception {
IMqttToken connectionResult = clientConnectWithNDEATH(ts, value);
MqttWireMessage response = connectionResult.getResponse();
Assert.assertEquals(MESSAGE_TYPE_CONNACK, response.getType());
MqttConnAck connAckMsg = (MqttConnAck) response;
Assert.assertEquals(MqttReturnCode.RETURN_CODE_SUCCESS, connAckMsg.getReturnCode());
}
public IMqttToken clientConnectWithNDEATH(long ts, long value, String... nameSpaceBad) throws Exception {
String key = keysBdSeq;
MetricDataType metricDataType = Int64;
SparkplugBProto.Payload.Builder deathPayload = SparkplugBProto.Payload.newBuilder()
@ -125,18 +133,15 @@ public abstract class AbstractMqttV5ClientSparkplugTest extends AbstractMqttInte
this.client.setCallback(this.mqttCallback);
MqttConnectionOptions options = new MqttConnectionOptions();
options.setUserName(gatewayAccessToken);
String topic = NAMESPACE + "/" + groupId + "/" + SparkplugMessageType.NDEATH.name() + "/" + edgeNode;
String nameSpace = nameSpaceBad.length == 0 ? NAMESPACE : nameSpaceBad[0];
String topic = nameSpace + "/" + groupId + "/" + SparkplugMessageType.NDEATH.name() + "/" + edgeNode;
MqttMessage msg = new MqttMessage();
msg.setId(0);
msg.setPayload(deathBytes);
options.setWill(topic, msg);
IMqttToken connectionResult = client.connect(options);
MqttWireMessage response = connectionResult.getResponse();
Assert.assertEquals(MESSAGE_TYPE_CONNACK, response.getType());
MqttConnAck connAckMsg = (MqttConnAck) response;
Assert.assertEquals(MqttReturnCode.RETURN_CODE_SUCCESS, connAckMsg.getReturnCode());
return client.connect(options);
}
protected List<Device> connectClientWithCorrectAccessTokenWithNDEATHCreatedDevices(int cntDevices, long ts) throws Exception {
List<Device> devices = new ArrayList<>();
clientWithCorrectNodeAccessTokenWithNDEATH();

View File

@ -33,6 +33,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataType.UInt32;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMessageType.NCMD;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopicUtil.NAMESPACE;
/**
* Created by nickAS21 on 12.01.23

View File

@ -40,6 +40,7 @@ import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugConn
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugConnectionState.ONLINE;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMessageType.STATE;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMessageType.messageName;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopicUtil.NAMESPACE;
/**
* Created by nickAS21 on 12.01.23
@ -74,6 +75,16 @@ public abstract class AbstractMqttV5ClientSparkplugConnectionTest extends Abstra
Assert.assertEquals(expectedReasonCode, actualException.getReasonCode());
}
protected void processClientWithCorrectNodeAccessTokenNameSpaceInvalid_Test() throws Exception {
long ts = calendar.getTimeInMillis() - PUBLISH_TS_DELTA_MS;
long value = bdSeq = 0;
MqttException actualException = Assert.assertThrows(MqttException.class, () -> clientConnectWithNDEATH(ts, value, "spBv1.2"));
String expectedMessage = "Server unavailable.";
int expectedReasonCode = 136;
Assert.assertEquals(expectedMessage, actualException.getMessage());
Assert.assertEquals(expectedReasonCode, actualException.getReasonCode());
}
protected void processClientWithCorrectAccessTokenWithNDEATHCreatedDevices(int cntDevices) throws Exception {
long ts = calendar.getTimeInMillis();
connectClientWithCorrectAccessTokenWithNDEATHCreatedDevices(cntDevices, ts);

View File

@ -49,6 +49,10 @@ public class MqttV5ClientSparkplugBConnectionTest extends AbstractMqttV5ClientSp
processClientWithCorrectNodeAccessTokenWithoutNDEATH_Test();
}
@Test
public void testClientWithCorrectNodeAccessTokenNameSpaceInvalid() throws Exception {
processClientWithCorrectNodeAccessTokenNameSpaceInvalid_Test();
}
@Test
public void testClientWithCorrectAccessTokenWithNDEATHCreatedOneDevice() throws Exception {

View File

@ -31,6 +31,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.thingsboard.server.common.data.exception.ThingsboardErrorCode.INVALID_ARGUMENTS;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMessageType.DCMD;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMessageType.NCMD;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopicUtil.NAMESPACE;
@Slf4j
public abstract class AbstractMqttV5RpcSparkplugTest extends AbstractMqttV5ClientSparkplugTest {

View File

@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.awaitility.Awaitility.await;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopicUtil.NAMESPACE;
/**
* Created by nickAS21 on 12.01.23

View File

@ -146,8 +146,6 @@ public enum MetricDataType {
}
/**
* Returns the class type for this DataType
*
* @return the class type for this DataType
*/
public Class<?> getClazz() {

View File

@ -25,10 +25,7 @@ public class SparkplugTopic {
/**
* The Sparkplug namespace version.
* For the Sparkplug A version of the payload definition, the UTF-8 string constant for the namespace element will be:
* spAv1.0
* For the Sparkplug B version of the specification, the UTF-8 string constant for the namespace element will be:
* spBv1.0
* For the Sparkplug B version of the specification, the UTF-8 string constant for the namespace element will be: spBv1.0
*/
private String namespace;
@ -105,9 +102,7 @@ public class SparkplugTopic {
}
/**
* Returns the Sparkplug namespace version.
*
* @return the namespace
* @return the Sparkplug namespace version
*/
public String getNamespace() {
return namespace;
@ -123,17 +118,13 @@ public class SparkplugTopic {
}
/**
* Returns the ID of the Edge of Network (EoN) Node.
*
* @return the edge node ID
* @return the ID of the Edge of Network (EoN) Node
*/
public String getEdgeNodeId() {
return edgeNodeId;
}
/**
* Returns the ID of the device.
*
* @return the device ID
*/
public String getDeviceId() {
@ -141,8 +132,6 @@ public class SparkplugTopic {
}
/**
* Returns the message type.
*
* @return the message type
*/
public SparkplugMessageType getType() {
@ -162,8 +151,6 @@ public class SparkplugTopic {
}
/**
* Returns true if this topic's type matches the passes in type, false otherwise.
*
* @param type the type to check
* @return true if this topic's type matches the passes in type, false otherwise
*/

View File

@ -30,6 +30,7 @@ public class SparkplugTopicUtil {
private static final Map<String, String[]> SPLIT_TOPIC_CACHE = new HashMap<String, String[]>();
private static final String TOPIC_INVALID_NUMBER = "Invalid number of topic elements: ";
public static final String NAMESPACE = "spBv1.0";
public static String[] getSplitTopic(String topic) {
String[] splitTopic = SPLIT_TOPIC_CACHE.get(topic);
@ -93,7 +94,7 @@ public class SparkplugTopicUtil {
} else {
SparkplugMessageType type;
String namespace, edgeNodeId, groupId, deviceId;
namespace = splitTopic[0];
namespace = validateNameSpace(splitTopic[0]);
groupId = length > 1 ? splitTopic[1] : null;
type = length > 2 ? SparkplugMessageType.parseMessageType(splitTopic[2]) : null;
edgeNodeId = length > 3 ? splitTopic[3] : null;
@ -102,4 +103,14 @@ public class SparkplugTopicUtil {
}
}
/**
* For the Sparkplug B version of the specification, the UTF-8 string constant for the namespace element will be: "spBv1.0"
* @param nameSpace
* @return
*/
private static String validateNameSpace(String nameSpace) throws ThingsboardException {
if ("spBv1.0".equals(nameSpace)) return nameSpace;
throw new ThingsboardException("The namespace [" + nameSpace + "] is not valid and must be [spBv1.0] for the Sparkplug™ B version.", ThingsboardErrorCode.INVALID_ARGUMENTS);
}
}