138 changed files with 2239 additions and 593 deletions
@ -0,0 +1,23 @@ |
|||
-- |
|||
-- 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. |
|||
-- |
|||
|
|||
-- FIX DASHBOARD TEMPLATES AFTER ANGULAR MIGRATION TO VER.15 |
|||
|
|||
UPDATE dashboard SET configuration = REPLACE(configuration, 'mat-button mat-icon-button', 'mat-icon-button') |
|||
WHERE configuration like '%mat-button mat-icon-button%'; |
|||
|
|||
UPDATE widget_type SET descriptor = REPLACE(descriptor, 'mat-button mat-icon-button', 'mat-icon-button') |
|||
WHERE descriptor like '%mat-button mat-icon-button%'; |
|||
@ -0,0 +1,96 @@ |
|||
/** |
|||
* 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.controller; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import org.junit.Assert; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
import org.thingsboard.server.common.data.queue.ProcessingStrategy; |
|||
import org.thingsboard.server.common.data.queue.ProcessingStrategyType; |
|||
import org.thingsboard.server.common.data.queue.Queue; |
|||
import org.thingsboard.server.common.data.queue.SubmitStrategy; |
|||
import org.thingsboard.server.common.data.queue.SubmitStrategyType; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@DaoSqlTest |
|||
public class BaseQueueControllerTest extends AbstractControllerTest { |
|||
|
|||
@Test |
|||
public void testQueueWithServiceTypeRE() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
// create queue
|
|||
Queue queue = new Queue(); |
|||
queue.setName("qwerty"); |
|||
queue.setTopic("tb_rule_engine.qwerty"); |
|||
queue.setPollInterval(25); |
|||
queue.setPartitions(10); |
|||
queue.setTenantId(TenantId.SYS_TENANT_ID); |
|||
queue.setConsumerPerPartition(false); |
|||
queue.setPackProcessingTimeout(2000); |
|||
SubmitStrategy submitStrategy = new SubmitStrategy(); |
|||
submitStrategy.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); |
|||
queue.setSubmitStrategy(submitStrategy); |
|||
ProcessingStrategy processingStrategy = new ProcessingStrategy(); |
|||
processingStrategy.setType(ProcessingStrategyType.RETRY_ALL); |
|||
processingStrategy.setRetries(3); |
|||
processingStrategy.setFailurePercentage(0.7); |
|||
processingStrategy.setPauseBetweenRetries(3); |
|||
processingStrategy.setMaxPauseBetweenRetries(5); |
|||
queue.setProcessingStrategy(processingStrategy); |
|||
|
|||
// create queue
|
|||
Queue queue2 = new Queue(); |
|||
queue2.setName("qwerty2"); |
|||
queue2.setTopic("tb_rule_engine.qwerty2"); |
|||
queue2.setPollInterval(25); |
|||
queue2.setPartitions(10); |
|||
queue2.setTenantId(TenantId.SYS_TENANT_ID); |
|||
queue2.setConsumerPerPartition(false); |
|||
queue2.setPackProcessingTimeout(2000); |
|||
submitStrategy.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); |
|||
queue2.setSubmitStrategy(submitStrategy); |
|||
processingStrategy.setType(ProcessingStrategyType.RETRY_ALL); |
|||
processingStrategy.setRetries(3); |
|||
processingStrategy.setFailurePercentage(0.7); |
|||
processingStrategy.setPauseBetweenRetries(3); |
|||
processingStrategy.setMaxPauseBetweenRetries(5); |
|||
queue2.setProcessingStrategy(processingStrategy); |
|||
|
|||
Queue savedQueue = doPost("/api/queues?serviceType=" + "TB-RULE-ENGINE", queue, Queue.class); |
|||
Queue savedQueue2 = doPost("/api/queues?serviceType=" + "TB_RULE_ENGINE", queue2, Queue.class); |
|||
|
|||
PageLink pageLink = new PageLink(10); |
|||
PageData<Queue> pageData; |
|||
pageData = doGetTypedWithPageLink("/api/queues?serviceType=TB-RULE-ENGINE&", new TypeReference<>() { |
|||
}, pageLink); |
|||
Assert.assertFalse(pageData.getData().isEmpty()); |
|||
doDelete("/api/queues/" + savedQueue.getUuidId()) |
|||
.andExpect(status().isOk()); |
|||
|
|||
pageData = doGetTypedWithPageLink("/api/queues?serviceType=TB_RULE_ENGINE&", new TypeReference<>() { |
|||
}, pageLink); |
|||
Assert.assertFalse(pageData.getData().isEmpty()); |
|||
doDelete("/api/queues/" + savedQueue2.getUuidId()) |
|||
.andExpect(status().isOk()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
/** |
|||
* 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.security.auth.oauth2; |
|||
|
|||
import org.junit.Test; |
|||
import org.mockito.Mockito; |
|||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; |
|||
|
|||
import javax.servlet.http.Cookie; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.Map; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.thingsboard.server.service.security.auth.oauth2.HttpCookieOAuth2AuthorizationRequestRepository.OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME; |
|||
|
|||
public class CookieUtilsTest { |
|||
|
|||
@Test |
|||
public void serializeDeserializeOAuth2AuthorizationRequestTest() { |
|||
HttpCookieOAuth2AuthorizationRequestRepository cookieRequestRepo = new HttpCookieOAuth2AuthorizationRequestRepository(); |
|||
HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class); |
|||
|
|||
Map<String, Object> additionalParameters = new LinkedHashMap<>(); |
|||
additionalParameters.put("param1", "value1"); |
|||
additionalParameters.put("param2", "value2"); |
|||
var request = OAuth2AuthorizationRequest.authorizationCode() |
|||
.authorizationUri("testUri").clientId("testId") |
|||
.scope("read", "write") |
|||
.additionalParameters(additionalParameters).build(); |
|||
|
|||
|
|||
Cookie cookie = new Cookie(OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, CookieUtils.serialize(request)); |
|||
Mockito.when(servletRequest.getCookies()).thenReturn(new Cookie[]{cookie}); |
|||
|
|||
OAuth2AuthorizationRequest deserializedRequest = cookieRequestRepo.loadAuthorizationRequest(servletRequest); |
|||
|
|||
assertNotNull(deserializedRequest); |
|||
assertEquals(request.getGrantType(), deserializedRequest.getGrantType()); |
|||
assertEquals(request.getAuthorizationUri(), deserializedRequest.getAuthorizationUri()); |
|||
assertEquals(request.getClientId(), deserializedRequest.getClientId()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,272 @@ |
|||
/** |
|||
* 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.common.data.util; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.leshan.core.LwM2m; |
|||
import org.eclipse.leshan.core.model.DDFFileValidator; |
|||
import org.eclipse.leshan.core.model.DefaultDDFFileValidator; |
|||
import org.eclipse.leshan.core.model.InvalidDDFFileException; |
|||
import org.eclipse.leshan.core.model.ObjectModel; |
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
import org.eclipse.leshan.core.util.StringUtils; |
|||
import org.w3c.dom.DOMException; |
|||
import org.w3c.dom.Document; |
|||
import org.w3c.dom.Node; |
|||
import org.w3c.dom.NodeList; |
|||
import org.xml.sax.SAXException; |
|||
|
|||
import javax.xml.parsers.DocumentBuilder; |
|||
import javax.xml.parsers.DocumentBuilderFactory; |
|||
import javax.xml.parsers.ParserConfigurationException; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
@Slf4j |
|||
public class TbDDFFileParser { |
|||
private static final DDFFileValidator ddfFileValidator = new DefaultDDFFileValidator(); |
|||
|
|||
public List<ObjectModel> parse(InputStream inputStream, String streamName) |
|||
throws InvalidDDFFileException, IOException { |
|||
streamName = streamName == null ? "" : streamName; |
|||
|
|||
log.debug("Parsing DDF file {}", streamName); |
|||
|
|||
try { |
|||
// Parse XML file
|
|||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
|||
factory.setNamespaceAware(true); |
|||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
|||
|
|||
DocumentBuilder builder = factory.newDocumentBuilder(); |
|||
Document document = builder.parse(inputStream); |
|||
|
|||
// Get DDF file validator
|
|||
LwM2m.LwM2mVersion lwm2mVersion = null; |
|||
ddfFileValidator.validate(document); |
|||
|
|||
// Build list of ObjectModel
|
|||
ArrayList<ObjectModel> objects = new ArrayList<>(); |
|||
NodeList nodeList = document.getDocumentElement().getElementsByTagName("Object"); |
|||
for (int i = 0; i < nodeList.getLength(); i++) { |
|||
objects.add(parseObject(nodeList.item(i), streamName, lwm2mVersion, true)); |
|||
} |
|||
return objects; |
|||
} catch (InvalidDDFFileException | SAXException e) { |
|||
throw new InvalidDDFFileException(e, "Invalid DDF file %s", streamName); |
|||
} |
|||
catch (ParserConfigurationException e) { |
|||
throw new IllegalStateException("Unable to create Document Builder", e); |
|||
} |
|||
} |
|||
|
|||
private ObjectModel parseObject(Node object, String streamName, LwM2m.LwM2mVersion schemaVersion, boolean validate) |
|||
throws InvalidDDFFileException { |
|||
|
|||
Node objectType = object.getAttributes().getNamedItem("ObjectType"); |
|||
if (validate && (objectType == null || !"MODefinition".equals(objectType.getTextContent()))) { |
|||
throw new InvalidDDFFileException( |
|||
"Object element in %s MUST have a ObjectType attribute equals to 'MODefinition'.", streamName); |
|||
} |
|||
|
|||
Integer id = null; |
|||
String name = null; |
|||
String description = null; |
|||
String version = ObjectModel.DEFAULT_VERSION; |
|||
Boolean multiple = null; |
|||
Boolean mandatory = null; |
|||
Map<Integer, ResourceModel> resources = new HashMap<>(); |
|||
String urn = null; |
|||
String description2 = null; |
|||
String lwm2mVersion = ObjectModel.DEFAULT_VERSION; |
|||
|
|||
for (int i = 0; i < object.getChildNodes().getLength(); i++) { |
|||
Node field = object.getChildNodes().item(i); |
|||
if (field.getNodeType() != Node.ELEMENT_NODE) |
|||
continue; |
|||
|
|||
switch (field.getNodeName()) { |
|||
case "ObjectID": |
|||
id = Integer.valueOf(field.getTextContent()); |
|||
break; |
|||
case "Name": |
|||
name = field.getTextContent(); |
|||
break; |
|||
case "Description1": |
|||
description = field.getTextContent(); |
|||
break; |
|||
case "ObjectVersion": |
|||
if (!StringUtils.isEmpty(field.getTextContent())) { |
|||
version = field.getTextContent(); |
|||
} |
|||
break; |
|||
case "MultipleInstances": |
|||
if ("Multiple".equals(field.getTextContent())) { |
|||
multiple = true; |
|||
} else if ("Single".equals(field.getTextContent())) { |
|||
multiple = false; |
|||
} |
|||
break; |
|||
case "Mandatory": |
|||
if ("Mandatory".equals(field.getTextContent())) { |
|||
mandatory = true; |
|||
} else if ("Optional".equals(field.getTextContent())) { |
|||
mandatory = false; |
|||
} |
|||
break; |
|||
case "Resources": |
|||
for (int j = 0; j < field.getChildNodes().getLength(); j++) { |
|||
Node item = field.getChildNodes().item(j); |
|||
if (item.getNodeType() != Node.ELEMENT_NODE) |
|||
continue; |
|||
|
|||
if (item.getNodeName().equals("Item")) { |
|||
ResourceModel resource = parseResource(item, streamName); |
|||
if (validate && resources.containsKey(resource.id)) { |
|||
throw new InvalidDDFFileException( |
|||
"Object %s in %s contains at least 2 resources with same id %s.", |
|||
id != null ? id : "", streamName, resource.id); |
|||
} else { |
|||
resources.put(resource.id, resource); |
|||
} |
|||
} |
|||
} |
|||
break; |
|||
case "ObjectURN": |
|||
urn = field.getTextContent(); |
|||
break; |
|||
case "LWM2MVersion": |
|||
if (!StringUtils.isEmpty(field.getTextContent())) { |
|||
lwm2mVersion = field.getTextContent(); |
|||
if (schemaVersion != null && !schemaVersion.toString().equals(lwm2mVersion)) { |
|||
throw new InvalidDDFFileException( |
|||
"LWM2MVersion is not consistent with xml shema(xsi:noNamespaceSchemaLocation) in %s : %s expected but was %s.", |
|||
streamName, schemaVersion, lwm2mVersion); |
|||
} |
|||
} |
|||
break; |
|||
case "Description2": |
|||
description2 = field.getTextContent(); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return new ObjectModel(id, name, description, version, multiple, mandatory, resources.values(), urn, |
|||
lwm2mVersion, description2); |
|||
|
|||
} |
|||
|
|||
private ResourceModel parseResource(Node item, String streamName) throws DOMException, InvalidDDFFileException { |
|||
|
|||
Integer id = Integer.valueOf(item.getAttributes().getNamedItem("ID").getTextContent()); |
|||
String name = null; |
|||
ResourceModel.Operations operations = null; |
|||
Boolean multiple = false; |
|||
Boolean mandatory = false; |
|||
ResourceModel.Type type = null; |
|||
String rangeEnumeration = null; |
|||
String units = null; |
|||
String description = null; |
|||
|
|||
for (int i = 0; i < item.getChildNodes().getLength(); i++) { |
|||
Node field = item.getChildNodes().item(i); |
|||
if (field.getNodeType() != Node.ELEMENT_NODE) |
|||
continue; |
|||
|
|||
switch (field.getNodeName()) { |
|||
case "Name": |
|||
name = field.getTextContent(); |
|||
break; |
|||
case "Operations": |
|||
String strOp = field.getTextContent(); |
|||
if (strOp != null && !strOp.isEmpty()) { |
|||
operations = ResourceModel.Operations.valueOf(strOp); |
|||
} else { |
|||
operations = ResourceModel.Operations.NONE; |
|||
} |
|||
break; |
|||
case "MultipleInstances": |
|||
if ("Multiple".equals(field.getTextContent())) { |
|||
multiple = true; |
|||
} else if ("Single".equals(field.getTextContent())) { |
|||
multiple = false; |
|||
} |
|||
break; |
|||
case "Mandatory": |
|||
if ("Mandatory".equals(field.getTextContent())) { |
|||
mandatory = true; |
|||
} else if ("Optional".equals(field.getTextContent())) { |
|||
mandatory = false; |
|||
} |
|||
break; |
|||
case "Type": |
|||
switch (field.getTextContent()) { |
|||
case "String": |
|||
type = ResourceModel.Type.STRING; |
|||
break; |
|||
case "Integer": |
|||
type = ResourceModel.Type.INTEGER; |
|||
break; |
|||
case "Float": |
|||
type = ResourceModel.Type.FLOAT; |
|||
break; |
|||
case "Boolean": |
|||
type = ResourceModel.Type.BOOLEAN; |
|||
break; |
|||
case "Opaque": |
|||
type = ResourceModel.Type.OPAQUE; |
|||
break; |
|||
case "Time": |
|||
type = ResourceModel.Type.TIME; |
|||
break; |
|||
case "Objlnk": |
|||
type = ResourceModel.Type.OBJLNK; |
|||
break; |
|||
case "Unsigned Integer": |
|||
type = ResourceModel.Type.UNSIGNED_INTEGER; |
|||
break; |
|||
case "Corelnk": |
|||
type = ResourceModel.Type.CORELINK; |
|||
break; |
|||
case "": |
|||
type = ResourceModel.Type.NONE; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
break; |
|||
case "RangeEnumeration": |
|||
rangeEnumeration = field.getTextContent(); |
|||
break; |
|||
case "Units": |
|||
units = field.getTextContent(); |
|||
break; |
|||
case "Description": |
|||
description = field.getTextContent(); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description); |
|||
} |
|||
} |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,17 @@ |
|||
# Security Policy |
|||
|
|||
## Reporting a Vulnerability |
|||
|
|||
Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to Thingsboard privately, |
|||
to minimize attacks against current users of Thingsboard before they are fixed. Vulnerabilities will be investigated and release as soon as possible. |
|||
|
|||
To report a vulnerability or a security-related issue, please email the private address security@thingsboard.io with the details of the vulnerability. |
|||
Emails will be addressed within 3 business days, including a detailed plan to investigate the issue and any potential workarounds to perform in the meantime. |
|||
Do not report non-security-impacting bugs through this channel. Use GitHub issues instead. |
|||
|
|||
**Proposed Email Content** |
|||
Provide a descriptive subject line and in the body of the email include the following information: |
|||
|
|||
- Basic identity information, such as your name and your affiliation or company. |
|||
- Detailed steps to reproduce the vulnerability (log errors, screenshots are all helpful to us). |
|||
- Description of the effects of the vulnerability on Thingsboard. |
|||
@ -0,0 +1,20 @@ |
|||
diff --git a/node_modules/@angular/core/fesm2020/core.mjs b/node_modules/@angular/core/fesm2020/core.mjs
|
|||
index 3e93015..9efcb96 100755
|
|||
--- a/node_modules/@angular/core/fesm2020/core.mjs
|
|||
+++ b/node_modules/@angular/core/fesm2020/core.mjs
|
|||
@@ -11053,13 +11053,13 @@ function findDirectiveDefMatches(tView, tNode) {
|
|||
if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) { |
|||
matches || (matches = []); |
|||
if (isComponentDef(def)) { |
|||
- if (ngDevMode) {
|
|||
+ // if (ngDevMode) {
|
|||
assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` + |
|||
`Please use a different tag to activate the ${stringify(def.type)} component.`); |
|||
if (isComponentHost(tNode)) { |
|||
throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type); |
|||
} |
|||
- }
|
|||
+ // }
|
|||
// Components are inserted at the front of the matches array so that their lifecycle |
|||
// hooks run before any directive lifecycle hooks. This appears to be for ViewEngine |
|||
// compatibility. This logic doesn't make sense with host directives, because it |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue