171 changed files with 5491 additions and 1865 deletions
@ -1,90 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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 lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.web.bind.annotation.PathVariable; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.thingsboard.server.common.data.Resource; |
|||
import org.thingsboard.server.common.data.ResourceType; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
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.dao.resource.ResourceService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
|
|||
@Slf4j |
|||
@RestController |
|||
@TbCoreComponent |
|||
@RequestMapping("/api") |
|||
public class ResourceController extends BaseController { |
|||
|
|||
private final ResourceService resourceService; |
|||
|
|||
public ResourceController(ResourceService resourceService) { |
|||
this.resourceService = resourceService; |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource", method = RequestMethod.POST) |
|||
@ResponseBody |
|||
public Resource saveResource(Resource resource) throws ThingsboardException { |
|||
try { |
|||
resource.setTenantId(getTenantId()); |
|||
Resource savedResource = checkNotNull(resourceService.saveResource(resource)); |
|||
tbClusterService.onResourceChange(savedResource, null); |
|||
return savedResource; |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public PageData<Resource> getResources(@RequestParam(required = false) boolean system, |
|||
@RequestParam int pageSize, |
|||
@RequestParam int page, |
|||
@RequestParam(required = false) String sortProperty, |
|||
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
|||
try { |
|||
PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder); |
|||
return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink)); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE) |
|||
@ResponseBody |
|||
public void deleteResource(@PathVariable("resourceType") ResourceType resourceType, |
|||
@PathVariable("resourceId") String resourceId) throws ThingsboardException { |
|||
try { |
|||
Resource resource = checkNotNull(resourceService.getResource(getTenantId(), resourceType, resourceId)); |
|||
resourceService.deleteResource(getTenantId(), resourceType, resourceId); |
|||
tbClusterService.onResourceDeleted(resource, null); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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 lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.core.io.ByteArrayResource; |
|||
import org.springframework.http.HttpHeaders; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.web.bind.annotation.PathVariable; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
import org.thingsboard.server.common.data.lwm2m.LwM2mObject; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.dao.resource.TbResourceService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.security.permission.Operation; |
|||
import org.thingsboard.server.service.security.permission.Resource; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Base64; |
|||
import java.util.List; |
|||
import java.util.StringJoiner; |
|||
|
|||
@Slf4j |
|||
@RestController |
|||
@TbCoreComponent |
|||
@RequestMapping("/api") |
|||
public class TbResourceController extends BaseController { |
|||
|
|||
public static final String RESOURCE_ID = "resourceId"; |
|||
|
|||
private final TbResourceService resourceService; |
|||
|
|||
public TbResourceController(TbResourceService resourceService) { |
|||
this.resourceService = resourceService; |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/{resourceId}/download", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public ResponseEntity<org.springframework.core.io.Resource> downloadResource(@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { |
|||
checkParameter(RESOURCE_ID, strResourceId); |
|||
try { |
|||
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); |
|||
TbResource tbResource = checkResourceId(resourceId, Operation.READ); |
|||
|
|||
ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); |
|||
return ResponseEntity.ok() |
|||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) |
|||
.header("x-filename", tbResource.getFileName()) |
|||
.contentLength(resource.contentLength()) |
|||
.contentType(MediaType.APPLICATION_OCTET_STREAM) |
|||
.body(resource); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/info/{resourceId}", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public TbResourceInfo getResourceInfoById(@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { |
|||
checkParameter(RESOURCE_ID, strResourceId); |
|||
try { |
|||
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); |
|||
return checkResourceInfoId(resourceId, Operation.READ); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public TbResource getResourceById(@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { |
|||
checkParameter(RESOURCE_ID, strResourceId); |
|||
try { |
|||
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); |
|||
return checkResourceId(resourceId, Operation.READ); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource", method = RequestMethod.POST) |
|||
@ResponseBody |
|||
public List<TbResource> saveResources(@RequestBody List<TbResource> resources) throws ThingsboardException { |
|||
try { |
|||
List<TbResource> addResources = new ArrayList<>(); |
|||
StringJoiner noSaveResources = new StringJoiner("; "); |
|||
resources.forEach(resource -> { |
|||
try { |
|||
resource.setTenantId(getTenantId()); |
|||
checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); |
|||
addResources.add(addResource(resource)); |
|||
} catch (Exception e) { |
|||
noSaveResources.add(resource.getFileName()); |
|||
log.warn("Fail save resource: [{}]", resource.getFileName(), e); |
|||
} |
|||
}); |
|||
if (noSaveResources.length() > 0) { |
|||
throw new ThingsboardException(String.format("Fail save resource: %s", noSaveResources.toString()), ThingsboardErrorCode.INVALID_ARGUMENTS); |
|||
} |
|||
return addResources; |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public PageData<TbResourceInfo> getResources(@RequestParam int pageSize, |
|||
@RequestParam int page, |
|||
@RequestParam(required = false) String textSearch, |
|||
@RequestParam(required = false) String sortProperty, |
|||
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
|||
try { |
|||
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
|||
if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { |
|||
return checkNotNull(resourceService.findTenantResourcesByTenantId(getTenantId(), pageLink)); |
|||
} else { |
|||
return checkNotNull(resourceService.findAllTenantResourcesByTenantId(getTenantId(), pageLink)); |
|||
} |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/lwm2m/page", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public List<LwM2mObject> getLwm2mListObjectsPage(@RequestParam int pageSize, |
|||
@RequestParam int page, |
|||
@RequestParam(required = false) String textSearch, |
|||
@RequestParam(required = false) String sortProperty, |
|||
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
|||
try { |
|||
PageLink pageLink = new PageLink(pageSize, page, textSearch); |
|||
return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET) |
|||
@ResponseBody |
|||
public List<LwM2mObject> getLwm2mListObjects(@RequestParam String sortOrder, |
|||
@RequestParam String sortProperty, |
|||
@RequestParam(required = false) String[] objectIds) throws ThingsboardException { |
|||
try { |
|||
return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.DELETE) |
|||
@ResponseBody |
|||
public void deleteResource(@PathVariable("resourceId") String strResourceId) throws ThingsboardException { |
|||
checkParameter("resourceId", strResourceId); |
|||
try { |
|||
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); |
|||
TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); |
|||
resourceService.deleteResource(getTenantId(), resourceId); |
|||
tbClusterService.onResourceDeleted(tbResource, null); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
private TbResource addResource(TbResource resource) throws Exception { |
|||
checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); |
|||
TbResource savedResource = checkNotNull(resourceService.saveResource(resource)); |
|||
tbClusterService.onResourceChange(savedResource, null); |
|||
return savedResource; |
|||
} |
|||
} |
|||
@ -0,0 +1,296 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.ResourceType; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
public abstract class BaseTbResourceControllerTest extends AbstractControllerTest { |
|||
|
|||
private IdComparator<TbResourceInfo> idComparator = new IdComparator<>(); |
|||
|
|||
private static final String DEFAULT_FILE_NAME = "test.jks"; |
|||
|
|||
private Tenant savedTenant; |
|||
private User tenantAdmin; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
Tenant tenant = new Tenant(); |
|||
tenant.setTitle("My tenant"); |
|||
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
|||
Assert.assertNotNull(savedTenant); |
|||
|
|||
tenantAdmin = new User(); |
|||
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
|||
tenantAdmin.setTenantId(savedTenant.getId()); |
|||
tenantAdmin.setEmail("tenant2@thingsboard.org"); |
|||
tenantAdmin.setFirstName("Joe"); |
|||
tenantAdmin.setLastName("Downs"); |
|||
|
|||
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testSaveTbResource() throws Exception { |
|||
TbResource resource = new TbResource(); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setTitle("My first resource"); |
|||
resource.setFileName(DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
|
|||
TbResource savedResource = save(resource); |
|||
|
|||
Assert.assertNotNull(savedResource); |
|||
Assert.assertNotNull(savedResource.getId()); |
|||
Assert.assertTrue(savedResource.getCreatedTime() > 0); |
|||
Assert.assertEquals(savedTenant.getId(), savedResource.getTenantId()); |
|||
Assert.assertEquals(resource.getTitle(), savedResource.getTitle()); |
|||
Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getFileName()); |
|||
Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getResourceKey()); |
|||
Assert.assertEquals(resource.getData(), savedResource.getData()); |
|||
|
|||
savedResource.setTitle("My new resource"); |
|||
|
|||
save(savedResource); |
|||
|
|||
TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); |
|||
Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateTbResourceFromDifferentTenant() throws Exception { |
|||
TbResource resource = new TbResource(); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setTitle("My first resource"); |
|||
resource.setFileName(DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
|
|||
TbResource savedResource = save(resource); |
|||
|
|||
loginDifferentTenant(); |
|||
doPostWithTypedResponse("/api/resource", Collections.singletonList(savedResource), new TypeReference<>(){}, status().isBadRequest()); |
|||
deleteDifferentTenant(); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindTbResourceById() throws Exception { |
|||
TbResource resource = new TbResource(); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setTitle("My first resource"); |
|||
resource.setFileName(DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
|
|||
TbResource savedResource = save(resource); |
|||
|
|||
TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); |
|||
Assert.assertNotNull(foundResource); |
|||
Assert.assertEquals(savedResource, foundResource); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteTbResource() throws Exception { |
|||
TbResource resource = new TbResource(); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setTitle("My first resource"); |
|||
resource.setFileName(DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
|
|||
TbResource savedResource = save(resource); |
|||
|
|||
doDelete("/api/resource/" + savedResource.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
|
|||
doGet("/api/resource/" + savedResource.getId().getId().toString()) |
|||
.andExpect(status().isNotFound()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindTenantTbResources() throws Exception { |
|||
List<TbResource> resourcesToSave = new ArrayList<>(); |
|||
for (int i = 0; i < 173; i++) { |
|||
TbResource resource = new TbResource(); |
|||
resource.setTitle("Resource" + i); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setFileName(i + DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
resourcesToSave.add(resource); |
|||
} |
|||
|
|||
List<TbResourceInfo> resources =save(resourcesToSave).stream().map(TbResourceInfo::new).collect(Collectors.toList()); |
|||
|
|||
List<TbResourceInfo> loadedResources = new ArrayList<>(); |
|||
PageLink pageLink = new PageLink(24); |
|||
PageData<TbResourceInfo> pageData; |
|||
do { |
|||
pageData = doGetTypedWithPageLink("/api/resource?", |
|||
new TypeReference<PageData<TbResourceInfo>>() { |
|||
}, pageLink); |
|||
loadedResources.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageLink.nextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Collections.sort(resources, idComparator); |
|||
Collections.sort(loadedResources, idComparator); |
|||
|
|||
Assert.assertEquals(resources, loadedResources); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindSystemTbResources() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
List<TbResourceInfo> resources = new ArrayList<>(); |
|||
for (int i = 0; i < 173; i++) { |
|||
TbResource resource = new TbResource(); |
|||
resource.setTitle("Resource" + i); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setFileName(i + DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
resources.add(new TbResourceInfo(save(resource))); |
|||
} |
|||
List<TbResourceInfo> loadedResources = new ArrayList<>(); |
|||
PageLink pageLink = new PageLink(24); |
|||
PageData<TbResourceInfo> pageData; |
|||
do { |
|||
pageData = doGetTypedWithPageLink("/api/resource?", |
|||
new TypeReference<PageData<TbResourceInfo>>() { |
|||
}, pageLink); |
|||
loadedResources.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageLink.nextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Collections.sort(resources, idComparator); |
|||
Collections.sort(loadedResources, idComparator); |
|||
|
|||
Assert.assertEquals(resources, loadedResources); |
|||
|
|||
for (TbResourceInfo resource : resources) { |
|||
doDelete("/api/resource/" + resource.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
} |
|||
|
|||
pageLink = new PageLink(27); |
|||
loadedResources.clear(); |
|||
do { |
|||
pageData = doGetTypedWithPageLink("/api/resource?", |
|||
new TypeReference<PageData<TbResourceInfo>>() { |
|||
}, pageLink); |
|||
loadedResources.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageLink.nextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Assert.assertTrue(loadedResources.isEmpty()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindSystemAndTenantTbResources() throws Exception { |
|||
List<TbResourceInfo> systemResources = new ArrayList<>(); |
|||
List<TbResourceInfo> expectedResources = new ArrayList<>(); |
|||
for (int i = 0; i < 73; i++) { |
|||
TbResource resource = new TbResource(); |
|||
resource.setTitle("Resource" + i); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setFileName(i + DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
expectedResources.add(new TbResourceInfo(save(resource))); |
|||
} |
|||
|
|||
loginSysAdmin(); |
|||
|
|||
for (int i = 0; i < 173; i++) { |
|||
TbResource resource = new TbResource(); |
|||
resource.setTitle("Resource" + i); |
|||
resource.setResourceType(ResourceType.JKS); |
|||
resource.setFileName(i + DEFAULT_FILE_NAME); |
|||
resource.setData("Test Data"); |
|||
TbResourceInfo savedResource = new TbResourceInfo(save(resource)); |
|||
systemResources.add(savedResource); |
|||
if (i >= 73) { |
|||
expectedResources.add(savedResource); |
|||
} |
|||
} |
|||
|
|||
login(tenantAdmin.getEmail(), "testPassword1"); |
|||
|
|||
List<TbResourceInfo> loadedResources = new ArrayList<>(); |
|||
PageLink pageLink = new PageLink(24); |
|||
PageData<TbResourceInfo> pageData; |
|||
do { |
|||
pageData = doGetTypedWithPageLink("/api/resource?", |
|||
new TypeReference<PageData<TbResourceInfo>>() { |
|||
}, pageLink); |
|||
loadedResources.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageLink.nextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Collections.sort(expectedResources, idComparator); |
|||
Collections.sort(loadedResources, idComparator); |
|||
|
|||
Assert.assertEquals(expectedResources, loadedResources); |
|||
|
|||
loginSysAdmin(); |
|||
|
|||
for (TbResourceInfo resource : systemResources) { |
|||
doDelete("/api/resource/" + resource.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
} |
|||
} |
|||
|
|||
private TbResource save(TbResource tbResource) throws Exception { |
|||
return save(Collections.singletonList(tbResource)).get(0); |
|||
} |
|||
|
|||
private List<TbResource> save(List<TbResource> tbResources) throws Exception { |
|||
return doPostWithTypedResponse("/api/resource", tbResources, new TypeReference<>(){}); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.sql; |
|||
|
|||
import org.thingsboard.server.controller.BaseTbResourceControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
@DaoSqlTest |
|||
public class TbResourceControllerSqlTest extends BaseTbResourceControllerTest { |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
|
|||
Copyright © 2016-2021 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. |
|||
|
|||
--> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.thingsboard</groupId> |
|||
<version>3.3.0-SNAPSHOT</version> |
|||
<artifactId>common</artifactId> |
|||
</parent> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<artifactId>coap-server</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>Thingsboard CoAP server</name> |
|||
<url>https://thingsboard.io</url> |
|||
|
|||
<properties> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
<main.dir>${basedir}/../..</main.dir> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<artifactId>queue</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<artifactId>data</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.thingsboard.common.transport</groupId> |
|||
<artifactId>transport-api</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
<scope>provided</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.eclipse.californium</groupId> |
|||
<artifactId>californium-core</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.eclipse.californium</groupId> |
|||
<artifactId>scandium</artifactId> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
|
|||
</project> |
|||
@ -0,0 +1,46 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coapserver; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Slf4j |
|||
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") |
|||
@Component |
|||
public class CoapServerContext { |
|||
|
|||
@Getter |
|||
@Value("${transport.coap.bind_address}") |
|||
private String host; |
|||
|
|||
@Getter |
|||
@Value("${transport.coap.bind_port}") |
|||
private Integer port; |
|||
|
|||
@Getter |
|||
@Value("${transport.coap.timeout}") |
|||
private Long timeout; |
|||
|
|||
@Getter |
|||
@Autowired(required = false) |
|||
private TbCoapDtlsSettings dtlsSettings; |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coapserver; |
|||
|
|||
import org.eclipse.californium.core.CoapServer; |
|||
|
|||
import java.net.UnknownHostException; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
|
|||
public interface CoapServerService { |
|||
|
|||
CoapServer getCoapServer() throws UnknownHostException; |
|||
|
|||
ConcurrentMap<String, TbCoapDtlsSessionInfo> getDtlsSessionsMap(); |
|||
|
|||
long getTimeout(); |
|||
|
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coapserver; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.californium.core.CoapServer; |
|||
import org.eclipse.californium.core.network.CoapEndpoint; |
|||
import org.eclipse.californium.core.network.config.NetworkConfig; |
|||
import org.eclipse.californium.core.server.resources.Resource; |
|||
import org.eclipse.californium.scandium.DTLSConnector; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
import java.net.InetAddress; |
|||
import java.net.InetSocketAddress; |
|||
import java.net.UnknownHostException; |
|||
import java.util.Random; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") |
|||
public class DefaultCoapServerService implements CoapServerService { |
|||
|
|||
@Autowired |
|||
private CoapServerContext coapServerContext; |
|||
|
|||
private CoapServer server; |
|||
|
|||
private TbCoapDtlsCertificateVerifier tbDtlsCertificateVerifier; |
|||
|
|||
private ScheduledExecutorService dtlsSessionsExecutor; |
|||
|
|||
@PostConstruct |
|||
public void init() throws UnknownHostException { |
|||
createCoapServer(); |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void shutdown() { |
|||
if (dtlsSessionsExecutor != null) { |
|||
dtlsSessionsExecutor.shutdownNow(); |
|||
} |
|||
log.info("Stopping CoAP server!"); |
|||
server.destroy(); |
|||
log.info("CoAP server stopped!"); |
|||
} |
|||
|
|||
@Override |
|||
public CoapServer getCoapServer() throws UnknownHostException { |
|||
if (server != null) { |
|||
return server; |
|||
} else { |
|||
return createCoapServer(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ConcurrentMap<String, TbCoapDtlsSessionInfo> getDtlsSessionsMap() { |
|||
return tbDtlsCertificateVerifier != null ? tbDtlsCertificateVerifier.getTbCoapDtlsSessionIdsMap() : null; |
|||
} |
|||
|
|||
@Override |
|||
public long getTimeout() { |
|||
return coapServerContext.getTimeout(); |
|||
} |
|||
|
|||
private CoapServer createCoapServer() throws UnknownHostException { |
|||
server = new CoapServer(); |
|||
|
|||
CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder(); |
|||
InetAddress addr = InetAddress.getByName(coapServerContext.getHost()); |
|||
InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); |
|||
noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr); |
|||
noSecCoapEndpointBuilder.setNetworkConfig(NetworkConfig.getStandard()); |
|||
CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build(); |
|||
server.addEndpoint(noSecCoapEndpoint); |
|||
|
|||
if (isDtlsEnabled()) { |
|||
CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder(); |
|||
TbCoapDtlsSettings dtlsSettings = coapServerContext.getDtlsSettings(); |
|||
DtlsConnectorConfig dtlsConnectorConfig = dtlsSettings.dtlsConnectorConfig(); |
|||
DTLSConnector connector = new DTLSConnector(dtlsConnectorConfig); |
|||
dtlsCoapEndpointBuilder.setConnector(connector); |
|||
CoapEndpoint dtlsCoapEndpoint = dtlsCoapEndpointBuilder.build(); |
|||
server.addEndpoint(dtlsCoapEndpoint); |
|||
if (dtlsConnectorConfig.isClientAuthenticationRequired()) { |
|||
tbDtlsCertificateVerifier = (TbCoapDtlsCertificateVerifier) dtlsConnectorConfig.getAdvancedCertificateVerifier(); |
|||
dtlsSessionsExecutor = Executors.newSingleThreadScheduledExecutor(); |
|||
dtlsSessionsExecutor.scheduleAtFixedRate(this::evictTimeoutSessions, new Random().nextInt((int) getDtlsSessionReportTimeout()), getDtlsSessionReportTimeout(), TimeUnit.MILLISECONDS); |
|||
} |
|||
} |
|||
Resource root = server.getRoot(); |
|||
TbCoapServerMessageDeliverer messageDeliverer = new TbCoapServerMessageDeliverer(root); |
|||
server.setMessageDeliverer(messageDeliverer); |
|||
|
|||
server.start(); |
|||
return server; |
|||
} |
|||
|
|||
private boolean isDtlsEnabled() { |
|||
return coapServerContext.getDtlsSettings() != null; |
|||
} |
|||
|
|||
private void evictTimeoutSessions() { |
|||
tbDtlsCertificateVerifier.evictTimeoutSessions(); |
|||
} |
|||
|
|||
private long getDtlsSessionReportTimeout() { |
|||
return tbDtlsCertificateVerifier.getDtlsSessionReportTimeout(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coapserver; |
|||
|
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.californium.elements.util.CertPathUtil; |
|||
import org.eclipse.californium.scandium.dtls.AlertMessage; |
|||
import org.eclipse.californium.scandium.dtls.CertificateMessage; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.CertificateVerificationResult; |
|||
import org.eclipse.californium.scandium.dtls.ConnectionId; |
|||
import org.eclipse.californium.scandium.dtls.DTLSSession; |
|||
import org.eclipse.californium.scandium.dtls.HandshakeException; |
|||
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; |
|||
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; |
|||
import org.eclipse.californium.scandium.util.ServerNames; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.msg.EncryptionUtil; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|||
import org.thingsboard.server.common.transport.auth.SessionInfoCreator; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.common.transport.util.SslUtil; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
|
|||
import javax.security.auth.x500.X500Principal; |
|||
import java.security.cert.CertPath; |
|||
import java.security.cert.CertificateEncodingException; |
|||
import java.security.cert.CertificateExpiredException; |
|||
import java.security.cert.CertificateNotYetValidException; |
|||
import java.security.cert.X509Certificate; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVerifier { |
|||
|
|||
private final TbCoapDtlsSessionInMemoryStorage tbCoapDtlsSessionInMemoryStorage; |
|||
|
|||
private TransportService transportService; |
|||
private TbServiceInfoProvider serviceInfoProvider; |
|||
private boolean skipValidityCheckForClientCert; |
|||
|
|||
public TbCoapDtlsCertificateVerifier(TransportService transportService, TbServiceInfoProvider serviceInfoProvider, long dtlsSessionInactivityTimeout, long dtlsSessionReportTimeout, boolean skipValidityCheckForClientCert) { |
|||
this.transportService = transportService; |
|||
this.serviceInfoProvider = serviceInfoProvider; |
|||
this.skipValidityCheckForClientCert = skipValidityCheckForClientCert; |
|||
this.tbCoapDtlsSessionInMemoryStorage = new TbCoapDtlsSessionInMemoryStorage(dtlsSessionInactivityTimeout, dtlsSessionReportTimeout); |
|||
} |
|||
|
|||
@Override |
|||
public List<CertificateType> getSupportedCertificateType() { |
|||
return Collections.singletonList(CertificateType.X_509); |
|||
} |
|||
|
|||
@Override |
|||
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) { |
|||
try { |
|||
String credentialsBody = null; |
|||
CertPath certpath = message.getCertificateChain(); |
|||
X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]); |
|||
for (X509Certificate cert : chain) { |
|||
try { |
|||
if (!skipValidityCheckForClientCert) { |
|||
cert.checkValidity(); |
|||
} |
|||
String strCert = SslUtil.getCertificateString(cert); |
|||
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); |
|||
final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1]; |
|||
CountDownLatch latch = new CountDownLatch(1); |
|||
transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(), |
|||
new TransportServiceCallback<>() { |
|||
@Override |
|||
public void onSuccess(ValidateDeviceCredentialsResponse msg) { |
|||
if (!StringUtils.isEmpty(msg.getCredentials())) { |
|||
deviceCredentialsResponse[0] = msg; |
|||
} |
|||
latch.countDown(); |
|||
} |
|||
|
|||
@Override |
|||
public void onError(Throwable e) { |
|||
log.error(e.getMessage(), e); |
|||
latch.countDown(); |
|||
} |
|||
}); |
|||
latch.await(10, TimeUnit.SECONDS); |
|||
ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0]; |
|||
if (msg != null && strCert.equals(msg.getCredentials())) { |
|||
credentialsBody = msg.getCredentials(); |
|||
DeviceProfile deviceProfile = msg.getDeviceProfile(); |
|||
if (msg.hasDeviceInfo() && deviceProfile != null) { |
|||
TransportProtos.SessionInfoProto sessionInfoProto = SessionInfoCreator.create(msg, serviceInfoProvider.getServiceId(), UUID.randomUUID()); |
|||
tbCoapDtlsSessionInMemoryStorage.put(session.getSessionIdentifier().toString(), new TbCoapDtlsSessionInfo(sessionInfoProto, deviceProfile)); |
|||
} |
|||
break; |
|||
} |
|||
} catch (InterruptedException | |
|||
CertificateEncodingException | |
|||
CertificateExpiredException | |
|||
CertificateNotYetValidException e) { |
|||
log.error(e.getMessage(), e); |
|||
} |
|||
} |
|||
if (credentialsBody == null) { |
|||
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.BAD_CERTIFICATE, |
|||
session.getPeer()); |
|||
throw new HandshakeException("Certificate chain could not be validated", alert); |
|||
} else { |
|||
return new CertificateVerificationResult(cid, certpath, null); |
|||
} |
|||
} catch (HandshakeException e) { |
|||
log.trace("Certificate validation failed!", e); |
|||
return new CertificateVerificationResult(cid, e, null); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public List<X500Principal> getAcceptedIssuers() { |
|||
return CertPathUtil.toSubjects(null); |
|||
} |
|||
|
|||
@Override |
|||
public void setResultHandler(HandshakeResultHandler resultHandler) { |
|||
// empty implementation
|
|||
} |
|||
|
|||
public ConcurrentMap<String, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionIdsMap() { |
|||
return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionIdMap(); |
|||
} |
|||
|
|||
public void evictTimeoutSessions() { |
|||
tbCoapDtlsSessionInMemoryStorage.evictTimeoutSessions(); |
|||
} |
|||
|
|||
public long getDtlsSessionReportTimeout() { |
|||
return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionReportTimeout(); |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coapserver; |
|||
|
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public class TbCoapDtlsSessionInMemoryStorage { |
|||
|
|||
private final ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap = new ConcurrentHashMap<>(); |
|||
private long dtlsSessionInactivityTimeout; |
|||
private long dtlsSessionReportTimeout; |
|||
|
|||
|
|||
public TbCoapDtlsSessionInMemoryStorage(long dtlsSessionInactivityTimeout, long dtlsSessionReportTimeout) { |
|||
this.dtlsSessionInactivityTimeout = dtlsSessionInactivityTimeout; |
|||
this.dtlsSessionReportTimeout = dtlsSessionReportTimeout; |
|||
} |
|||
|
|||
public void put(String dtlsSessionId, TbCoapDtlsSessionInfo dtlsSessionInfo) { |
|||
log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", dtlsSessionId, dtlsSessionInfo.getLastActivityTime()); |
|||
dtlsSessionIdMap.putIfAbsent(dtlsSessionId, dtlsSessionInfo); |
|||
} |
|||
|
|||
public void evictTimeoutSessions() { |
|||
long expTime = System.currentTimeMillis() - dtlsSessionInactivityTimeout; |
|||
dtlsSessionIdMap.entrySet().removeIf(entry -> { |
|||
if (entry.getValue().getLastActivityTime() < expTime) { |
|||
log.trace("DTLS session was removed from in-memory store: [{}]", entry.getKey()); |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.coapserver; |
|||
|
|||
import com.google.common.io.Resources; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.californium.elements.util.SslContextUtil; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.InetAddress; |
|||
import java.net.InetSocketAddress; |
|||
import java.net.UnknownHostException; |
|||
import java.security.GeneralSecurityException; |
|||
import java.security.cert.Certificate; |
|||
import java.util.Collections; |
|||
import java.util.Optional; |
|||
|
|||
@Slf4j |
|||
@ConditionalOnExpression("'${transport.coap.enabled}'=='true'") |
|||
@ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
@Component |
|||
public class TbCoapDtlsSettings { |
|||
|
|||
@Value("${transport.coap.dtls.bind_address}") |
|||
private String host; |
|||
|
|||
@Value("${transport.coap.dtls.bind_port}") |
|||
private Integer port; |
|||
|
|||
@Value("${transport.coap.dtls.mode}") |
|||
private String mode; |
|||
|
|||
@Value("${transport.coap.dtls.key_store}") |
|||
private String keyStoreFile; |
|||
|
|||
@Value("${transport.coap.dtls.key_store_password}") |
|||
private String keyStorePassword; |
|||
|
|||
@Value("${transport.coap.dtls.key_password}") |
|||
private String keyPassword; |
|||
|
|||
@Value("${transport.coap.dtls.key_alias}") |
|||
private String keyAlias; |
|||
|
|||
@Value("${transport.coap.dtls.skip_validity_check_for_client_cert}") |
|||
private boolean skipValidityCheckForClientCert; |
|||
|
|||
@Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout}") |
|||
private long dtlsSessionInactivityTimeout; |
|||
|
|||
@Value("${transport.coap.dtls.x509.dtls_session_report_timeout}") |
|||
private long dtlsSessionReportTimeout; |
|||
|
|||
@Autowired |
|||
private TransportService transportService; |
|||
|
|||
@Autowired |
|||
private TbServiceInfoProvider serviceInfoProvider; |
|||
|
|||
public DtlsConnectorConfig dtlsConnectorConfig() throws UnknownHostException { |
|||
Optional<SecurityMode> securityModeOpt = SecurityMode.parse(mode); |
|||
if (securityModeOpt.isEmpty()) { |
|||
log.warn("Incorrect configuration of securityMode {}", mode); |
|||
throw new RuntimeException("Failed to parse mode property: " + mode + "!"); |
|||
} else { |
|||
DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(); |
|||
configBuilder.setAddress(getInetSocketAddress()); |
|||
String keyStoreFilePath = Resources.getResource(keyStoreFile).getPath(); |
|||
SslContextUtil.Credentials serverCredentials = loadServerCredentials(keyStoreFilePath); |
|||
SecurityMode securityMode = securityModeOpt.get(); |
|||
if (securityMode.equals(SecurityMode.NO_AUTH)) { |
|||
configBuilder.setClientAuthenticationRequired(false); |
|||
configBuilder.setServerOnly(true); |
|||
} else { |
|||
configBuilder.setAdvancedCertificateVerifier( |
|||
new TbCoapDtlsCertificateVerifier( |
|||
transportService, |
|||
serviceInfoProvider, |
|||
dtlsSessionInactivityTimeout, |
|||
dtlsSessionReportTimeout, |
|||
skipValidityCheckForClientCert |
|||
) |
|||
); |
|||
} |
|||
configBuilder.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), |
|||
Collections.singletonList(CertificateType.X_509)); |
|||
return configBuilder.build(); |
|||
} |
|||
} |
|||
|
|||
private SslContextUtil.Credentials loadServerCredentials(String keyStoreFilePath) { |
|||
try { |
|||
return SslContextUtil.loadCredentials(keyStoreFilePath, keyAlias, keyStorePassword.toCharArray(), |
|||
keyPassword.toCharArray()); |
|||
} catch (GeneralSecurityException | IOException e) { |
|||
throw new RuntimeException("Failed to load serverCredentials due to: ", e); |
|||
} |
|||
} |
|||
|
|||
private void loadTrustedCertificates(DtlsConnectorConfig.Builder config, String keyStoreFilePath) { |
|||
StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder(); |
|||
try { |
|||
Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates( |
|||
keyStoreFilePath, keyAlias, |
|||
keyStorePassword.toCharArray()); |
|||
trustBuilder.setTrustedCertificates(trustedCertificates); |
|||
if (trustBuilder.hasTrusts()) { |
|||
config.setAdvancedCertificateVerifier(trustBuilder.build()); |
|||
} |
|||
} catch (GeneralSecurityException | IOException e) { |
|||
throw new RuntimeException("Failed to load trusted certificates due to: ", e); |
|||
} |
|||
} |
|||
|
|||
private InetSocketAddress getInetSocketAddress() throws UnknownHostException { |
|||
InetAddress addr = InetAddress.getByName(host); |
|||
return new InetSocketAddress(addr, port); |
|||
} |
|||
|
|||
private enum SecurityMode { |
|||
X509, |
|||
NO_AUTH; |
|||
|
|||
static Optional<SecurityMode> parse(String name) { |
|||
SecurityMode mode = null; |
|||
if (name != null) { |
|||
for (SecurityMode securityMode : SecurityMode.values()) { |
|||
if (securityMode.name().equalsIgnoreCase(name)) { |
|||
mode = securityMode; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return Optional.ofNullable(mode); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.dao.resource; |
|||
|
|||
import org.eclipse.leshan.core.model.InvalidDDFFileException; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.ResourceType; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.lwm2m.LwM2mObject; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.List; |
|||
|
|||
|
|||
public interface TbResourceService { |
|||
TbResource saveResource(TbResource resource) throws InvalidDDFFileException, IOException; |
|||
|
|||
TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); |
|||
|
|||
TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); |
|||
|
|||
TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); |
|||
|
|||
PageData<TbResourceInfo> findAllTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); |
|||
|
|||
PageData<TbResourceInfo> findTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); |
|||
|
|||
List<LwM2mObject> findLwM2mObject(TenantId tenantId, |
|||
String sortOrder, |
|||
String sortProperty, |
|||
String[] objectIds); |
|||
|
|||
List<LwM2mObject> findLwM2mObjectPage(TenantId tenantId, |
|||
String sortProperty, |
|||
String sortOrder, |
|||
PageLink pageLink); |
|||
|
|||
void deleteResource(TenantId tenantId, TbResourceId resourceId); |
|||
|
|||
void deleteResourcesByTenantId(TenantId tenantId); |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class TbResource extends TbResourceInfo { |
|||
|
|||
private static final long serialVersionUID = 7379609705527272306L; |
|||
|
|||
private String fileName; |
|||
|
|||
private String data; |
|||
|
|||
public TbResource() { |
|||
super(); |
|||
} |
|||
|
|||
public TbResource(TbResourceId id) { |
|||
super(id); |
|||
} |
|||
|
|||
public TbResource(TbResourceInfo resourceInfo) { |
|||
super(resourceInfo); |
|||
} |
|||
|
|||
public TbResource(TbResource resource) { |
|||
super(resource); |
|||
this.data = resource.getData(); |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
StringBuilder builder = new StringBuilder(); |
|||
builder.append("Resource [tenantId="); |
|||
builder.append(getTenantId()); |
|||
builder.append(", id="); |
|||
builder.append(getUuidId()); |
|||
builder.append(", createdTime="); |
|||
builder.append(createdTime); |
|||
builder.append(", title="); |
|||
builder.append(getTitle()); |
|||
builder.append(", resourceType="); |
|||
builder.append(getResourceType()); |
|||
builder.append(", resourceKey="); |
|||
builder.append(getResourceKey()); |
|||
builder.append(", fileName="); |
|||
builder.append(fileName); |
|||
builder.append(", data="); |
|||
builder.append(data); |
|||
builder.append("]"); |
|||
return builder.toString(); |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasTenantId { |
|||
|
|||
private TenantId tenantId; |
|||
private String title; |
|||
private ResourceType resourceType; |
|||
private String resourceKey; |
|||
private String searchText; |
|||
|
|||
public TbResourceInfo() { |
|||
super(); |
|||
} |
|||
|
|||
public TbResourceInfo(TbResourceId id) { |
|||
super(id); |
|||
} |
|||
|
|||
public TbResourceInfo(TbResourceInfo resourceInfo) { |
|||
super(resourceInfo); |
|||
this.tenantId = resourceInfo.getTenantId(); |
|||
this.title = resourceInfo.getTitle(); |
|||
this.resourceType = resourceInfo.getResourceType(); |
|||
this.resourceKey = resourceInfo.getResourceKey(); |
|||
this.searchText = resourceInfo.getSearchText(); |
|||
} |
|||
|
|||
@Override |
|||
public String getSearchText() { |
|||
return searchText != null ? searchText : title; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
StringBuilder builder = new StringBuilder(); |
|||
builder.append("ResourceInfo [tenantId="); |
|||
builder.append(tenantId); |
|||
builder.append(", id="); |
|||
builder.append(getUuidId()); |
|||
builder.append(", createdTime="); |
|||
builder.append(createdTime); |
|||
builder.append(", title="); |
|||
builder.append(title); |
|||
builder.append(", resourceType="); |
|||
builder.append(resourceType); |
|||
builder.append(", resourceKey="); |
|||
builder.append(resourceKey); |
|||
builder.append("]"); |
|||
return builder.toString(); |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.id; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonCreator; |
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public class TbResourceId extends UUIDBased implements EntityId { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@JsonCreator |
|||
public TbResourceId(@JsonProperty("id") UUID id) { |
|||
super(id); |
|||
} |
|||
|
|||
@JsonIgnore |
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.TB_RESOURCE; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.lwm2m; |
|||
|
|||
public interface LwM2mConstants { |
|||
|
|||
String LWM2M_SEPARATOR_PATH = "/"; |
|||
String LWM2M_SEPARATOR_KEY = "_"; |
|||
String LWM2M_SEPARATOR_SEARCH_TEXT = ":"; |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.validation; |
|||
|
|||
import javax.validation.Constraint; |
|||
import javax.validation.Payload; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target(ElementType.FIELD) |
|||
@Constraint(validatedBy = {}) |
|||
public @interface NoXss { |
|||
String message() default "field value is malformed"; |
|||
|
|||
Class<?>[] groups() default {}; |
|||
|
|||
Class<? extends Payload>[] payload() default {}; |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.transport.coap.client; |
|||
|
|||
import org.eclipse.californium.core.CoapClient; |
|||
import org.eclipse.californium.core.CoapResponse; |
|||
import org.eclipse.californium.core.Utils; |
|||
import org.eclipse.californium.elements.DtlsEndpointContext; |
|||
import org.eclipse.californium.elements.EndpointContext; |
|||
import org.eclipse.californium.elements.exception.ConnectorException; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
import java.security.Principal; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
public class NoSecClient { |
|||
|
|||
private ExecutorService executor = Executors.newFixedThreadPool(1); |
|||
private CoapClient coapClient; |
|||
|
|||
public NoSecClient(String host, int port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
URI uri = new URI(getFutureUrl(host, port, accessToken, clientKeys, sharedKeys)); |
|||
this.coapClient = new CoapClient(uri); |
|||
} |
|||
|
|||
public void test() { |
|||
executor.submit(() -> { |
|||
try { |
|||
while (!Thread.interrupted()) { |
|||
CoapResponse response = null; |
|||
try { |
|||
response = coapClient.get(); |
|||
} catch (ConnectorException | IOException e) { |
|||
System.err.println("Error occurred while sending request: " + e); |
|||
System.exit(-1); |
|||
} |
|||
if (response != null) { |
|||
|
|||
System.out.println(response.getCode() + " - " + response.getCode().name()); |
|||
System.out.println(response.getOptions()); |
|||
System.out.println(response.getResponseText()); |
|||
System.out.println(); |
|||
System.out.println("ADVANCED:"); |
|||
EndpointContext context = response.advanced().getSourceContext(); |
|||
Principal identity = context.getPeerIdentity(); |
|||
if (identity != null) { |
|||
System.out.println(context.getPeerIdentity()); |
|||
} else { |
|||
System.out.println("anonymous"); |
|||
} |
|||
System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER)); |
|||
System.out.println(Utils.prettyPrint(response)); |
|||
} else { |
|||
System.out.println("No response received."); |
|||
} |
|||
Thread.sleep(5000); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error occurred while sending COAP requests."); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private String getFutureUrl(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) { |
|||
return "coap://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys; |
|||
} |
|||
|
|||
public static void main(String[] args) throws URISyntaxException { |
|||
System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.NoSecClient " + |
|||
"host port accessToken clientKeys sharedKeys"); |
|||
|
|||
String host = args[0]; |
|||
int port = Integer.parseInt(args[1]); |
|||
String accessToken = args[2]; |
|||
String clientKeys = args[3]; |
|||
String sharedKeys = args[4]; |
|||
|
|||
NoSecClient client = new NoSecClient(host, port, accessToken, clientKeys, sharedKeys); |
|||
client.test(); |
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.transport.coap.client; |
|||
|
|||
import org.eclipse.californium.core.CoapClient; |
|||
import org.eclipse.californium.core.CoapResponse; |
|||
import org.eclipse.californium.core.Utils; |
|||
import org.eclipse.californium.core.network.CoapEndpoint; |
|||
import org.eclipse.californium.elements.DtlsEndpointContext; |
|||
import org.eclipse.californium.elements.EndpointContext; |
|||
import org.eclipse.californium.elements.exception.ConnectorException; |
|||
import org.eclipse.californium.elements.util.SslContextUtil; |
|||
import org.eclipse.californium.scandium.DTLSConnector; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
import java.security.GeneralSecurityException; |
|||
import java.security.Principal; |
|||
import java.security.cert.Certificate; |
|||
import java.util.Collections; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
public class SecureClientNoAuth { |
|||
|
|||
private final DTLSConnector dtlsConnector; |
|||
private ExecutorService executor = Executors.newFixedThreadPool(1); |
|||
private CoapClient coapClient; |
|||
|
|||
public SecureClientNoAuth(DTLSConnector dtlsConnector, String host, int port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
this.dtlsConnector = dtlsConnector; |
|||
this.coapClient = getCoapClient(host, port, accessToken, clientKeys, sharedKeys); |
|||
} |
|||
|
|||
public void test() { |
|||
executor.submit(() -> { |
|||
try { |
|||
while (!Thread.interrupted()) { |
|||
CoapResponse response = null; |
|||
try { |
|||
response = coapClient.get(); |
|||
} catch (ConnectorException | IOException e) { |
|||
System.err.println("Error occurred while sending request: " + e); |
|||
System.exit(-1); |
|||
} |
|||
if (response != null) { |
|||
|
|||
System.out.println(response.getCode() + " - " + response.getCode().name()); |
|||
System.out.println(response.getOptions()); |
|||
System.out.println(response.getResponseText()); |
|||
System.out.println(); |
|||
System.out.println("ADVANCED:"); |
|||
EndpointContext context = response.advanced().getSourceContext(); |
|||
Principal identity = context.getPeerIdentity(); |
|||
if (identity != null) { |
|||
System.out.println(context.getPeerIdentity()); |
|||
} else { |
|||
System.out.println("anonymous"); |
|||
} |
|||
System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER)); |
|||
System.out.println(Utils.prettyPrint(response)); |
|||
} else { |
|||
System.out.println("No response received."); |
|||
} |
|||
Thread.sleep(5000); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error occurred while sending COAP requests."); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private CoapClient getCoapClient(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
URI uri = new URI(getFutureUrl(host, port, accessToken, clientKeys, sharedKeys)); |
|||
CoapClient client = new CoapClient(uri); |
|||
CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); |
|||
builder.setConnector(dtlsConnector); |
|||
|
|||
client.setEndpoint(builder.build()); |
|||
return client; |
|||
} |
|||
|
|||
private String getFutureUrl(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) { |
|||
return "coaps://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys; |
|||
} |
|||
|
|||
public static void main(String[] args) throws URISyntaxException { |
|||
System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.SecureClientNoAuth " + |
|||
"host port accessToken keyStoreUriPath keyStoreAlias trustedAliasPattern clientKeys sharedKeys"); |
|||
|
|||
String host = args[0]; |
|||
int port = Integer.parseInt(args[1]); |
|||
String accessToken = args[2]; |
|||
String clientKeys = args[7]; |
|||
String sharedKeys = args[8]; |
|||
|
|||
String keyStoreUriPath = args[3]; |
|||
String keyStoreAlias = args[4]; |
|||
String trustedAliasPattern = args[5]; |
|||
String keyStorePassword = args[6]; |
|||
|
|||
|
|||
DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); |
|||
setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword); |
|||
DTLSConnector dtlsConnector = new DTLSConnector(builder.build()); |
|||
SecureClientNoAuth client = new SecureClientNoAuth(dtlsConnector, host, port, accessToken, clientKeys, sharedKeys); |
|||
client.test(); |
|||
} |
|||
|
|||
private static void setupCredentials(DtlsConnectorConfig.Builder config, String keyStoreUriPath, String keyStoreAlias, String trustedAliasPattern, String keyStorePassword) { |
|||
StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder(); |
|||
try { |
|||
SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials( |
|||
keyStoreUriPath, keyStoreAlias, keyStorePassword.toCharArray(), keyStorePassword.toCharArray()); |
|||
Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates( |
|||
keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray()); |
|||
trustBuilder.setTrustedCertificates(trustedCertificates); |
|||
config.setAdvancedCertificateVerifier(trustBuilder.build()); |
|||
config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)); |
|||
} catch (GeneralSecurityException e) { |
|||
System.err.println("certificates are invalid!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} catch (IOException e) { |
|||
System.err.println("certificates are missing!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,144 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.transport.coap.client; |
|||
|
|||
import org.eclipse.californium.core.CoapClient; |
|||
import org.eclipse.californium.core.CoapResponse; |
|||
import org.eclipse.californium.core.Utils; |
|||
import org.eclipse.californium.core.network.CoapEndpoint; |
|||
import org.eclipse.californium.elements.DtlsEndpointContext; |
|||
import org.eclipse.californium.elements.EndpointContext; |
|||
import org.eclipse.californium.elements.exception.ConnectorException; |
|||
import org.eclipse.californium.elements.util.SslContextUtil; |
|||
import org.eclipse.californium.scandium.DTLSConnector; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
import java.security.GeneralSecurityException; |
|||
import java.security.Principal; |
|||
import java.security.cert.Certificate; |
|||
import java.util.Collections; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
public class SecureClientX509 { |
|||
|
|||
private final DTLSConnector dtlsConnector; |
|||
private ExecutorService executor = Executors.newFixedThreadPool(1); |
|||
private CoapClient coapClient; |
|||
|
|||
public SecureClientX509(DTLSConnector dtlsConnector, String host, int port, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
this.dtlsConnector = dtlsConnector; |
|||
this.coapClient = getCoapClient(host, port, clientKeys, sharedKeys); |
|||
} |
|||
|
|||
public void test() { |
|||
executor.submit(() -> { |
|||
try { |
|||
while (!Thread.interrupted()) { |
|||
CoapResponse response = null; |
|||
try { |
|||
response = coapClient.get(); |
|||
} catch (ConnectorException | IOException e) { |
|||
System.err.println("Error occurred while sending request: " + e); |
|||
System.exit(-1); |
|||
} |
|||
if (response != null) { |
|||
|
|||
System.out.println(response.getCode() + " - " + response.getCode().name()); |
|||
System.out.println(response.getOptions()); |
|||
System.out.println(response.getResponseText()); |
|||
System.out.println(); |
|||
System.out.println("ADVANCED:"); |
|||
EndpointContext context = response.advanced().getSourceContext(); |
|||
Principal identity = context.getPeerIdentity(); |
|||
if (identity != null) { |
|||
System.out.println(context.getPeerIdentity()); |
|||
} else { |
|||
System.out.println("anonymous"); |
|||
} |
|||
System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER)); |
|||
System.out.println(Utils.prettyPrint(response)); |
|||
} else { |
|||
System.out.println("No response received."); |
|||
} |
|||
Thread.sleep(5000); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error occurred while sending COAP requests."); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private CoapClient getCoapClient(String host, Integer port, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
URI uri = new URI(getFutureUrl(host, port, clientKeys, sharedKeys)); |
|||
CoapClient client = new CoapClient(uri); |
|||
CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); |
|||
builder.setConnector(dtlsConnector); |
|||
|
|||
client.setEndpoint(builder.build()); |
|||
return client; |
|||
} |
|||
|
|||
private String getFutureUrl(String host, Integer port, String clientKeys, String sharedKeys) { |
|||
return "coaps://" + host + ":" + port + "/api/v1/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys; |
|||
} |
|||
|
|||
public static void main(String[] args) throws URISyntaxException { |
|||
System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.SecureClientX509 " + |
|||
"host port keyStoreUriPath keyStoreAlias trustedAliasPattern clientKeys sharedKeys"); |
|||
|
|||
String host = args[0]; |
|||
int port = Integer.parseInt(args[1]); |
|||
String clientKeys = args[6]; |
|||
String sharedKeys = args[7]; |
|||
|
|||
String keyStoreUriPath = args[2]; |
|||
String keyStoreAlias = args[3]; |
|||
String trustedAliasPattern = args[4]; |
|||
String keyStorePassword = args[5]; |
|||
|
|||
|
|||
DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); |
|||
setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword); |
|||
DTLSConnector dtlsConnector = new DTLSConnector(builder.build()); |
|||
SecureClientX509 client = new SecureClientX509(dtlsConnector, host, port, clientKeys, sharedKeys); |
|||
client.test(); |
|||
} |
|||
|
|||
private static void setupCredentials(DtlsConnectorConfig.Builder config, String keyStoreUriPath, String keyStoreAlias, String trustedAliasPattern, String keyStorePassword) { |
|||
StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder(); |
|||
try { |
|||
SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials( |
|||
keyStoreUriPath, keyStoreAlias, keyStorePassword.toCharArray(), keyStorePassword.toCharArray()); |
|||
Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates( |
|||
keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray()); |
|||
trustBuilder.setTrustedCertificates(trustedCertificates); |
|||
config.setAdvancedCertificateVerifier(trustBuilder.build()); |
|||
config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)); |
|||
} catch (GeneralSecurityException e) { |
|||
System.err.println("certificates are invalid!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} catch (IOException e) { |
|||
System.err.println("certificates are missing!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} |
|||
} |
|||
} |
|||
@ -1,251 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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.transport.lwm2m.server.store; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.leshan.core.util.Hex; |
|||
import org.eclipse.leshan.server.registration.Registration; |
|||
import org.eclipse.leshan.server.security.InMemorySecurityStore; |
|||
import org.eclipse.leshan.server.security.SecurityInfo; |
|||
import org.eclipse.leshan.server.security.SecurityStoreListener; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; |
|||
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; |
|||
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; |
|||
import org.thingsboard.server.transport.lwm2m.utils.TypeServer; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.locks.Lock; |
|||
import java.util.concurrent.locks.ReadWriteLock; |
|||
import java.util.concurrent.locks.ReentrantReadWriteLock; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; |
|||
|
|||
@Slf4j |
|||
//@Service("LwM2mInMemorySecurityStore")
|
|||
//@TbLwM2mTransportComponent
|
|||
@Deprecated |
|||
public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { |
|||
private static final boolean INFOS_ARE_COMPROMISED = false; |
|||
|
|||
// lock for the two maps
|
|||
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); |
|||
private final Lock readLock = readWriteLock.readLock(); |
|||
private final Lock writeLock = readWriteLock.writeLock(); |
|||
private final Map<String /** registrationId */, LwM2mClient> sessions = new ConcurrentHashMap<>(); |
|||
private Map<UUID /** profileUUid */, LwM2mClientProfile> profiles = new ConcurrentHashMap<>(); |
|||
private SecurityStoreListener listener; |
|||
|
|||
@Autowired |
|||
LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator; |
|||
|
|||
/** |
|||
* Start after DefaultAuthorizer or LwM2mPskStore |
|||
* @param endPoint - |
|||
* @return SecurityInfo |
|||
*/ |
|||
@Override |
|||
public SecurityInfo getByEndpoint(String endPoint) { |
|||
readLock.lock(); |
|||
try { |
|||
String registrationId = this.getRegistrationId(endPoint, null); |
|||
return (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? |
|||
sessions.get(registrationId).getSecurityInfo() : this.addLwM2MClientToSession(endPoint); |
|||
} finally { |
|||
readLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Start after LwM2mPskStore |
|||
* @param identity - |
|||
* @return SecurityInfo |
|||
*/ |
|||
@Override |
|||
public SecurityInfo getByIdentity(String identity) { |
|||
readLock.lock(); |
|||
try { |
|||
String integrationId = this.getRegistrationId(null, identity); |
|||
return (integrationId != null) ? sessions.get(integrationId).getSecurityInfo() : this.addLwM2MClientToSession(identity); |
|||
} finally { |
|||
readLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public Collection<SecurityInfo> getAll() { |
|||
readLock.lock(); |
|||
try { |
|||
return this.sessions.values().stream().map(LwM2mClient::getSecurityInfo).collect(Collectors.toUnmodifiableList()); |
|||
} finally { |
|||
readLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Removed registration Client from sessions and listener |
|||
* @param registrationId if Client |
|||
*/ |
|||
public void delRemoveSessionAndListener(String registrationId) { |
|||
writeLock.lock(); |
|||
try { |
|||
LwM2mClient lwM2MClient = (sessions.get(registrationId) != null) ? sessions.get(registrationId) : null; |
|||
if (lwM2MClient != null) { |
|||
if (listener != null) { |
|||
listener.securityInfoRemoved(INFOS_ARE_COMPROMISED, lwM2MClient.getSecurityInfo()); |
|||
} |
|||
sessions.remove(registrationId); |
|||
} |
|||
} finally { |
|||
writeLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void setListener(SecurityStoreListener listener) { |
|||
this.listener = listener; |
|||
} |
|||
|
|||
public LwM2mClient getLwM2MClient(String endPoint, String identity) { |
|||
Map.Entry<String, LwM2mClient> modelClients = endPoint != null ? |
|||
this.sessions.entrySet().stream().filter(model -> endPoint.equals(model.getValue().getEndpoint())).findAny().orElse(null) : |
|||
this.sessions.entrySet().stream().filter(model -> identity.equals(model.getValue().getIdentity())).findAny().orElse(null); |
|||
return modelClients != null ? modelClients.getValue() : null; |
|||
} |
|||
|
|||
public LwM2mClient getLwM2MClientWithReg(Registration registration, String registrationId) { |
|||
return registrationId != null ? |
|||
this.sessions.get(registrationId) : |
|||
this.sessions.containsKey(registration.getId()) ? |
|||
this.sessions.get(registration.getId()) : |
|||
this.sessions.get(registration.getEndpoint()); |
|||
} |
|||
|
|||
public LwM2mClient getLwM2MClient(TransportProtos.SessionInfoProto sessionInfo) { |
|||
return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); |
|||
} |
|||
|
|||
/** |
|||
* Update in sessions (LwM2MClient for key registration_Id) after starting registration LwM2MClient in LwM2MTransportServiceImpl |
|||
* Remove from sessions LwM2MClient with key registration_Endpoint |
|||
* @param registration - |
|||
* @return LwM2MClient after adding it to session |
|||
*/ |
|||
public LwM2mClient updateInSessionsLwM2MClient(Registration registration) { |
|||
writeLock.lock(); |
|||
try { |
|||
if (this.sessions.get(registration.getEndpoint()) == null) { |
|||
this.addLwM2MClientToSession(registration.getEndpoint()); |
|||
} |
|||
LwM2mClient lwM2MClient = this.sessions.get(registration.getEndpoint()); |
|||
lwM2MClient.setRegistration(registration); |
|||
// lwM2MClient.getAttributes().putAll(registration.getAdditionalRegistrationAttributes());
|
|||
this.sessions.remove(registration.getEndpoint()); |
|||
this.sessions.put(registration.getId(), lwM2MClient); |
|||
return lwM2MClient; |
|||
} finally { |
|||
writeLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
private String getRegistrationId(String endPoint, String identity) { |
|||
List<String> registrationIds = (endPoint != null) ? |
|||
this.sessions.entrySet().stream().filter(model -> endPoint.equals(model.getValue().getEndpoint())).map(Map.Entry::getKey).collect(Collectors.toList()) : |
|||
this.sessions.entrySet().stream().filter(model -> identity.equals(model.getValue().getIdentity())).map(Map.Entry::getKey).collect(Collectors.toList()); |
|||
return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null; |
|||
} |
|||
|
|||
public Registration getByRegistration(String registrationId) { |
|||
return this.sessions.get(registrationId).getRegistration(); |
|||
} |
|||
|
|||
/** |
|||
* Add new LwM2MClient to session |
|||
* @param identity- |
|||
* @return SecurityInfo. If error - SecurityInfoError |
|||
* and log: |
|||
* - FORBIDDEN - if there is no authorization |
|||
* - profileUuid - if the device does not have a profile |
|||
* - device - if the thingsboard does not have a device with a name equal to the identity |
|||
*/ |
|||
private SecurityInfo addLwM2MClientToSession(String identity) { |
|||
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, TypeServer.CLIENT); |
|||
if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { |
|||
UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; |
|||
if (store.getSecurityInfo() != null && profileUuid != null) { |
|||
String endpoint = store.getSecurityInfo().getEndpoint(); |
|||
sessions.put(endpoint, new LwM2mClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), profileUuid, UUID.randomUUID())); |
|||
} else if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) { |
|||
sessions.put(identity, new LwM2mClient(identity, null, null, store.getMsg(), profileUuid, UUID.randomUUID())); |
|||
} else { |
|||
log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity); |
|||
/** |
|||
* Return Error securityInfo |
|||
*/ |
|||
byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray()); |
|||
SecurityInfo infoError = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey); |
|||
return infoError; |
|||
} |
|||
} |
|||
return store.getSecurityInfo(); |
|||
} |
|||
|
|||
public Map<String, LwM2mClient> getSession(UUID sessionUuId) { |
|||
return this.sessions.entrySet().stream() |
|||
.filter(e -> e.getValue().getSessionId().equals(sessionUuId)) |
|||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|||
} |
|||
|
|||
public Map<String, LwM2mClient> getSessions() { |
|||
return this.sessions; |
|||
} |
|||
|
|||
public Map<UUID, LwM2mClientProfile> getProfiles() { |
|||
return this.profiles; |
|||
} |
|||
|
|||
public LwM2mClientProfile getProfile(UUID profileUuId) { |
|||
return this.profiles.get(profileUuId); |
|||
} |
|||
|
|||
public LwM2mClientProfile getProfile(String registrationId) { |
|||
UUID profileUUid = this.getSessions().get(registrationId).getProfileId(); |
|||
return this.getProfiles().get(profileUUid); |
|||
} |
|||
|
|||
public Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles) { |
|||
return this.profiles = profiles; |
|||
} |
|||
|
|||
public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) { |
|||
LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandler.getLwM2MClientProfileFromThingsboard(deviceProfile); |
|||
if (lwM2MClientProfile != null) { |
|||
profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue