added new hash_code column to resource table
This commit is contained in:
parent
31cf975495
commit
f356a94b81
@ -52,3 +52,10 @@ $$
|
|||||||
$$;
|
$$;
|
||||||
|
|
||||||
-- NOTIFICATION CONFIGS VERSION CONTROL END
|
-- NOTIFICATION CONFIGS VERSION CONTROL END
|
||||||
|
|
||||||
|
ALTER TABLE resource
|
||||||
|
ADD COLUMN IF NOT EXISTS hash_code varchar;
|
||||||
|
|
||||||
|
UPDATE resource
|
||||||
|
SET hash_code = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null;
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import io.swagger.annotations.ApiParam;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
import org.springframework.http.CacheControl;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@ -29,6 +30,7 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -47,10 +49,10 @@ import org.thingsboard.server.common.data.page.PageLink;
|
|||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||||
import org.thingsboard.server.service.resource.TbResourceService;
|
import org.thingsboard.server.service.resource.TbResourceService;
|
||||||
|
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||||
import org.thingsboard.server.service.security.permission.Operation;
|
import org.thingsboard.server.service.security.permission.Operation;
|
||||||
import org.thingsboard.server.service.security.permission.Resource;
|
import org.thingsboard.server.service.security.permission.Resource;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -89,45 +91,47 @@ public class TbResourceController extends BaseController {
|
|||||||
@RequestMapping(value = "/resource/{resourceId}/download", method = RequestMethod.GET)
|
@RequestMapping(value = "/resource/{resourceId}/download", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<org.springframework.core.io.Resource> downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
public ResponseEntity<org.springframework.core.io.Resource> downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||||
@PathVariable(RESOURCE_ID) String strResourceId, HttpServletRequest request) throws ThingsboardException {
|
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
|
||||||
checkParameter(RESOURCE_ID, strResourceId);
|
checkParameter(RESOURCE_ID, strResourceId);
|
||||||
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
|
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
|
||||||
TbResource tbResource = checkResourceId(resourceId, Operation.READ);
|
TbResource tbResource = checkResourceId(resourceId, Operation.READ);
|
||||||
|
|
||||||
ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes()));
|
ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes()));
|
||||||
|
|
||||||
HashCode hashCode = Hashing.sha256().hashBytes(resource.getByteArray());
|
|
||||||
String ifNoneMatch = request.getHeader("If-None-Match");
|
|
||||||
if (ifNoneMatch != null) {
|
|
||||||
if (ifNoneMatch.equals(hashCode.toString())) {
|
|
||||||
return ResponseEntity.status(HttpStatus.NOT_MODIFIED)
|
|
||||||
.eTag(hashCode.toString()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String mediaType;
|
|
||||||
switch (tbResource.getResourceType()) {
|
|
||||||
case LWM2M_MODEL:
|
|
||||||
mediaType = "application/xml";
|
|
||||||
break;
|
|
||||||
case JKS:
|
|
||||||
mediaType = "application/x-java-keystore";
|
|
||||||
break;
|
|
||||||
case PKCS_12:
|
|
||||||
mediaType = "application/x-pkcs12";
|
|
||||||
break;
|
|
||||||
default: mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName())
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName())
|
||||||
.header("x-filename", tbResource.getFileName())
|
.header("x-filename", tbResource.getFileName())
|
||||||
.contentLength(resource.contentLength())
|
.contentLength(resource.contentLength())
|
||||||
.header("Content-Type", mediaType)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.eTag(hashCode.toString())
|
|
||||||
.body(resource);
|
.body(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||||
|
@RequestMapping(value = "/resource/lwm2m/{resourceId}/download", method = RequestMethod.GET)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<org.springframework.core.io.Resource> downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||||
|
@PathVariable(RESOURCE_ID) String strResourceId, @RequestHeader HttpHeaders headers) throws ThingsboardException {
|
||||||
|
return downloadResourceIfChanged(ResourceType.LWM2M_MODEL, strResourceId, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||||
|
@RequestMapping(value = "/resource/pkcs12/{resourceId}/download", method = RequestMethod.GET)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<org.springframework.core.io.Resource> downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||||
|
@PathVariable(RESOURCE_ID) String strResourceId, HttpHeaders headers) throws ThingsboardException {
|
||||||
|
return downloadResourceIfChanged(ResourceType.PKCS_12, strResourceId, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||||
|
@RequestMapping(value = "/resource/js/{resourceId}/download", method = RequestMethod.GET)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<org.springframework.core.io.Resource> downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||||
|
@PathVariable(RESOURCE_ID) String strResourceId, HttpHeaders headers) throws ThingsboardException {
|
||||||
|
return downloadResourceIfChanged(ResourceType.JS_MODULE, strResourceId, headers);
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation(value = "Get Resource Info (getResourceInfoById)",
|
@ApiOperation(value = "Get Resource Info (getResourceInfoById)",
|
||||||
notes = "Fetch the Resource Info object based on the provided Resource Id. " +
|
notes = "Fetch the Resource Info object based on the provided Resource Id. " +
|
||||||
RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
|
RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
|
||||||
@ -257,4 +261,31 @@ public class TbResourceController extends BaseController {
|
|||||||
TbResource tbResource = checkResourceId(resourceId, Operation.DELETE);
|
TbResource tbResource = checkResourceId(resourceId, Operation.DELETE);
|
||||||
tbResourceService.delete(tbResource, getCurrentUser());
|
tbResourceService.delete(tbResource, getCurrentUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<org.springframework.core.io.Resource> downloadResourceIfChanged(ResourceType type, String strResourceId, HttpHeaders headers) throws ThingsboardException {
|
||||||
|
checkParameter(RESOURCE_ID, strResourceId);
|
||||||
|
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
|
||||||
|
TbResourceInfo tbResourceInfo = checkResourceInfoId(resourceId, Operation.READ);
|
||||||
|
|
||||||
|
List<String> ifNoneMatchHeaders = headers.getIfNoneMatch();
|
||||||
|
if (!ifNoneMatchHeaders.isEmpty()) {
|
||||||
|
if (ifNoneMatchHeaders.contains(tbResourceInfo.getHashCode())) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_MODIFIED)
|
||||||
|
.eTag(tbResourceInfo.getHashCode()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityUser currentUser = getCurrentUser();
|
||||||
|
TbResource tbResource = resourceService.findResourceById(currentUser.getTenantId(), resourceId);
|
||||||
|
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())
|
||||||
|
.header("Content-Type", type.mediaType)
|
||||||
|
.cacheControl(CacheControl.noCache())
|
||||||
|
.eTag(tbResource.getHashCode())
|
||||||
|
.body(resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.resource;
|
package org.thingsboard.server.service.resource;
|
||||||
|
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
@ -36,6 +38,7 @@ import org.thingsboard.server.dao.resource.ResourceService;
|
|||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||||
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
|
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -166,6 +169,8 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
|
|||||||
} else {
|
} else {
|
||||||
resource.setResourceKey(resource.getFileName());
|
resource.setResourceKey(resource.getFileName());
|
||||||
}
|
}
|
||||||
|
HashCode hashCode = Hashing.sha256().hashBytes(Base64.getDecoder().decode(resource.getData().getBytes()));
|
||||||
|
resource.setHashCode(hashCode.toString());
|
||||||
return resourceService.saveResource(resource);
|
return resourceService.saveResource(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,5 +16,16 @@
|
|||||||
package org.thingsboard.server.common.data;
|
package org.thingsboard.server.common.data;
|
||||||
|
|
||||||
public enum ResourceType {
|
public enum ResourceType {
|
||||||
LWM2M_MODEL, JKS, PKCS_12, JS_MODULE
|
LWM2M_MODEL("lwm2m", "application/xml"),
|
||||||
|
JKS("jks", "application/x-java-keystore"),
|
||||||
|
PKCS_12("pkcs12", "application/x-pkcs12"),
|
||||||
|
JS_MODULE("js", "application/javascript");
|
||||||
|
|
||||||
|
public String type;
|
||||||
|
public String mediaType;
|
||||||
|
|
||||||
|
ResourceType(String type, String mediaType) {
|
||||||
|
this.type = type;
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,6 +74,8 @@ public class TbResource extends TbResourceInfo {
|
|||||||
builder.append(fileName);
|
builder.append(fileName);
|
||||||
builder.append(", data=");
|
builder.append(", data=");
|
||||||
builder.append(data);
|
builder.append(data);
|
||||||
|
builder.append(", hashCode=");
|
||||||
|
builder.append(getHashCode());
|
||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,8 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
|
|||||||
private String resourceKey;
|
private String resourceKey;
|
||||||
@ApiModelProperty(position = 7, value = "Resource search text.", example = "19_1.0:binaryappdatacontainer", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
@ApiModelProperty(position = 7, value = "Resource search text.", example = "19_1.0:binaryappdatacontainer", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||||
private String searchText;
|
private String searchText;
|
||||||
|
@ApiModelProperty(position = 8, value = "Resource hash code.", example = "33a64df551425fcc55e4d42a148795d9f25f89d4", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||||
|
private String hashCode;
|
||||||
|
|
||||||
public TbResourceInfo() {
|
public TbResourceInfo() {
|
||||||
super();
|
super();
|
||||||
@ -64,6 +66,7 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
|
|||||||
this.resourceType = resourceInfo.getResourceType();
|
this.resourceType = resourceInfo.getResourceType();
|
||||||
this.resourceKey = resourceInfo.getResourceKey();
|
this.resourceKey = resourceInfo.getResourceKey();
|
||||||
this.searchText = resourceInfo.getSearchText();
|
this.searchText = resourceInfo.getSearchText();
|
||||||
|
this.hashCode = resourceInfo.getHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " +
|
@ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " +
|
||||||
@ -107,6 +110,8 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
|
|||||||
builder.append(resourceType);
|
builder.append(resourceType);
|
||||||
builder.append(", resourceKey=");
|
builder.append(", resourceKey=");
|
||||||
builder.append(resourceKey);
|
builder.append(resourceKey);
|
||||||
|
builder.append(", hashCode=");
|
||||||
|
builder.append(hashCode);
|
||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -480,6 +480,7 @@ public class ModelConstants {
|
|||||||
public static final String RESOURCE_TITLE_COLUMN = TITLE_PROPERTY;
|
public static final String RESOURCE_TITLE_COLUMN = TITLE_PROPERTY;
|
||||||
public static final String RESOURCE_FILE_NAME_COLUMN = "file_name";
|
public static final String RESOURCE_FILE_NAME_COLUMN = "file_name";
|
||||||
public static final String RESOURCE_DATA_COLUMN = "data";
|
public static final String RESOURCE_DATA_COLUMN = "data";
|
||||||
|
public static final String RESOURCE_HASH_CODE_COLUMN = "hash_code";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ota Package constants.
|
* Ota Package constants.
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN;
|
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN;
|
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN;
|
||||||
|
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_HASH_CODE_COLUMN;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN;
|
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME;
|
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
|
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
|
||||||
@ -65,6 +66,9 @@ public class TbResourceEntity extends BaseSqlEntity<TbResource> implements BaseE
|
|||||||
@Column(name = RESOURCE_DATA_COLUMN)
|
@Column(name = RESOURCE_DATA_COLUMN)
|
||||||
private String data;
|
private String data;
|
||||||
|
|
||||||
|
@Column(name = RESOURCE_HASH_CODE_COLUMN)
|
||||||
|
private String hashCode;
|
||||||
|
|
||||||
public TbResourceEntity() {
|
public TbResourceEntity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +86,7 @@ public class TbResourceEntity extends BaseSqlEntity<TbResource> implements BaseE
|
|||||||
this.searchText = resource.getSearchText();
|
this.searchText = resource.getSearchText();
|
||||||
this.fileName = resource.getFileName();
|
this.fileName = resource.getFileName();
|
||||||
this.data = resource.getData();
|
this.data = resource.getData();
|
||||||
|
this.hashCode = resource.getHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -95,6 +100,7 @@ public class TbResourceEntity extends BaseSqlEntity<TbResource> implements BaseE
|
|||||||
resource.setSearchText(searchText);
|
resource.setSearchText(searchText);
|
||||||
resource.setFileName(fileName);
|
resource.setFileName(fileName);
|
||||||
resource.setData(data);
|
resource.setData(data);
|
||||||
|
resource.setHashCode(hashCode);
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user