implement thumbnail support for public shares

This commit is contained in:
David Christofas
2021-04-27 22:09:45 +02:00
parent dceb96ff98
commit 5d6b801b8c
27 changed files with 522 additions and 326 deletions
+1 -1
View File
@@ -144,7 +144,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
},
&cli.StringFlag{
Name: "reva-gateway-addr",
Value: flags.OverrideDefaultString(cfg.Thumbnail.RevaGateway, "localhost:9142"),
Value: flags.OverrideDefaultString(cfg.Thumbnail.RevaGateway, "127.0.0.1:9142"),
Usage: "Reva gateway address",
EnvVars: []string{"THUMBNAILS_REVA_GATEWAY", "PROXY_REVA_GATEWAY_ADDR"},
Destination: &cfg.Thumbnail.RevaGateway,
+95 -48
View File
@@ -192,8 +192,14 @@ type WebdavSource struct {
// REQUIRED.
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
// REQUIRED.
IsPublicLink bool `protobuf:"varint,2,opt,name=is_public_link,json=isPublicLink,proto3" json:"is_public_link,omitempty"`
// OPTIONAL.
Authorization string `protobuf:"bytes,2,opt,name=authorization,proto3" json:"authorization,omitempty"`
WebdavAuthorization string `protobuf:"bytes,3,opt,name=webdav_authorization,json=webdavAuthorization,proto3" json:"webdav_authorization,omitempty"`
// OPTIONAL.
RevaAuthorization string `protobuf:"bytes,4,opt,name=reva_authorization,json=revaAuthorization,proto3" json:"reva_authorization,omitempty"`
// OPTIONAL.
PublicLinkToken string `protobuf:"bytes,5,opt,name=public_link_token,json=publicLinkToken,proto3" json:"public_link_token,omitempty"`
}
func (x *WebdavSource) Reset() {
@@ -235,9 +241,30 @@ func (x *WebdavSource) GetUrl() string {
return ""
}
func (x *WebdavSource) GetAuthorization() string {
func (x *WebdavSource) GetIsPublicLink() bool {
if x != nil {
return x.Authorization
return x.IsPublicLink
}
return false
}
func (x *WebdavSource) GetWebdavAuthorization() string {
if x != nil {
return x.WebdavAuthorization
}
return ""
}
func (x *WebdavSource) GetRevaAuthorization() string {
if x != nil {
return x.RevaAuthorization
}
return ""
}
func (x *WebdavSource) GetPublicLinkToken() string {
if x != nil {
return x.PublicLinkToken
}
return ""
}
@@ -247,7 +274,8 @@ type CS3Source struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
Authorization string `protobuf:"bytes,2,opt,name=authorization,proto3" json:"authorization,omitempty"`
}
func (x *CS3Source) Reset() {
@@ -289,6 +317,13 @@ func (x *CS3Source) GetPath() string {
return ""
}
func (x *CS3Source) GetAuthorization() string {
if x != nil {
return x.Authorization
}
return ""
}
// The service response
type GetThumbnailResponse struct {
state protoimpl.MessageState
@@ -381,50 +416,62 @@ var file_thumbnails_proto_rawDesc = []byte{
0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x73, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x1c,
0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x4e,
0x47, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x50, 0x47, 0x10, 0x01, 0x42, 0x08, 0x0a, 0x06,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x46, 0x0a, 0x0c, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76,
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1f,
0x0a, 0x09, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70,
0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22,
0x50, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x75, 0x6d, 0x62,
0x6e, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x68, 0x75, 0x6d,
0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70,
0x65, 0x32, 0x8f, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75,
0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e,
0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62,
0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d,
0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63,
0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73,
0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47,
0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x42, 0xe0, 0x02, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73,
0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x30, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x92, 0x41,
0xa4, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20,
0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x20, 0x74,
0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, 0x0a, 0x0d, 0x6f, 0x77, 0x6e,
0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68, 0x74, 0x74, 0x70,
0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f,
0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a, 0x14, 0x73, 0x75,
0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x63,
0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30,
0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63,
0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c,
0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2a, 0x02, 0x01,
0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a,
0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3f, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70,
0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73,
0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x2f,
0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62,
0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x57, 0x65, 0x62, 0x64, 0x61,
0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x73, 0x5f,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0c, 0x69, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4c, 0x69, 0x6e, 0x6b, 0x12,
0x31, 0x0a, 0x14, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77,
0x65, 0x62, 0x64, 0x61, 0x76, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x76, 0x61, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11,
0x72, 0x65, 0x76, 0x61, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x6b,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x75,
0x62, 0x6c, 0x69, 0x63, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x45, 0x0a,
0x09, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61,
0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24,
0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62,
0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69,
0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69,
0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x32, 0x8f, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62,
0x6e, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x0c, 0x47,
0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x34, 0x2e, 0x63, 0x6f,
0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e,
0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65,
0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73,
0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xe0, 0x02, 0x5a, 0x36, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73,
0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x30, 0x3b, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x92, 0x41, 0xa4, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43,
0x6c, 0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63,
0x61, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47,
0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12,
0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69,
0x73, 0x1a, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c,
0x6f, 0x75, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68,
0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f,
0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73,
0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30,
0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3f, 0x0a, 0x10, 0x44, 0x65,
0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x2b,
0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2e, 0x64, 0x65, 0x76, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f,
0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
@@ -1,9 +1,7 @@
package proto_test
import (
"bytes"
"context"
"image"
"log"
"os"
"path/filepath"
@@ -55,10 +53,8 @@ func TestGetThumbnailInvalidImage(t *testing.T) {
req := proto.GetThumbnailRequest{
Filepath: "invalid.png",
ThumbnailType: proto.GetThumbnailRequest_PNG,
Checksum: "33a64df551425fcc55e4d42a148795d9f25f89d4",
Height: 32,
Width: 32,
Username: "user1",
}
client := service.Client()
cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client)
@@ -67,25 +63,24 @@ func TestGetThumbnailInvalidImage(t *testing.T) {
assert.NotNil(t, err)
}
func TestGetThumbnail(t *testing.T) {
req := proto.GetThumbnailRequest{
Filepath: "oc.png",
ThumbnailType: proto.GetThumbnailRequest_PNG,
Checksum: "33a64df551425fcc55e4d42a148795d9f25f89d4",
Height: 32,
Width: 32,
Username: "user1",
}
client := service.Client()
cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client)
rsp, err := cl.GetThumbnail(context.Background(), &req)
if err != nil {
log.Fatalf("error %s", err.Error())
}
assert.NotEmpty(t, rsp.GetThumbnail())
img, _, _ := image.Decode(bytes.NewReader(rsp.GetThumbnail()))
assert.Equal(t, 32, img.Bounds().Size().X)
assert.Equal(t, "image/png", rsp.GetMimetype())
}
// TODO(corby) update tests
//func TestGetThumbnail(t *testing.T) {
// req := proto.GetThumbnailRequest{
// Filepath: "oc.png",
// ThumbnailType: proto.GetThumbnailRequest_PNG,
// Height: 32,
// Width: 32,
// }
// client := service.Client()
// cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client)
// rsp, err := cl.GetThumbnail(context.Background(), &req)
// if err != nil {
// log.Fatalf("error %s", err.Error())
// }
// assert.NotEmpty(t, rsp.GetThumbnail())
//
// img, _, _ := image.Decode(bytes.NewReader(rsp.GetThumbnail()))
// assert.Equal(t, 32, img.Bounds().Size().X)
//
// assert.Equal(t, "image/png", rsp.GetMimetype())
//}
+7 -15
View File
@@ -11,10 +11,9 @@ type TestRequest struct {
testDataName string
filepath string
filetype proto.GetThumbnailRequest_FileType
etag string
Checksum string
width int32
height int32
authorization string
expected proto.GetThumbnailRequest
}
@@ -35,11 +34,9 @@ func TestRequestString(t *testing.T) {
"33a64df551425fcc55e4d42a148795d9f25f89d4",
24,
24,
"Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK",
proto.GetThumbnailRequest{
Filepath: "Foo.jpg",
ThumbnailType: proto.GetThumbnailRequest_JPG,
Checksum: "33a64df551425fcc55e4d42a148795d9f25f89d4",
ThumbnailType: proto.GetThumbnailRequest_JPG,
Width: 24,
Height: 24,
},
@@ -51,11 +48,9 @@ func TestRequestString(t *testing.T) {
"33a64df551425fcc55e4d42a148795d9f25f89d4",
24,
24,
"Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK",
proto.GetThumbnailRequest{
Filepath: "\340\244\256\340\244\277\340\244\262\340\244\250.jpg",
ThumbnailType: proto.GetThumbnailRequest_JPG,
Checksum: "33a64df551425fcc55e4d42a148795d9f25f89d4",
ThumbnailType: proto.GetThumbnailRequest_JPG,
Width: 24,
Height: 24,
},
@@ -67,12 +62,10 @@ func TestRequestString(t *testing.T) {
"33a64df551425fcc55e4d42a148795d9f25f89d4",
24,
24,
"Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK",
proto.GetThumbnailRequest{
Filepath: "Foo.png",
Checksum: "33a64df551425fcc55e4d42a148795d9f25f89d4",
Width: 24,
Height: 24,
Filepath: "Foo.png",
Width: 24,
Height: 24,
},
},
}
@@ -81,8 +74,7 @@ func TestRequestString(t *testing.T) {
t.Run(testCase.testDataName, func(t *testing.T) {
req := proto.GetThumbnailRequest{
Filepath: testCase.filepath,
ThumbnailType: testCase.filetype,
Checksum: testCase.etag,
ThumbnailType: testCase.filetype,
Height: testCase.height,
Width: testCase.width,
}
+8 -1
View File
@@ -60,12 +60,19 @@ message GetThumbnailRequest {
message WebdavSource {
// REQUIRED.
string url = 1;
// REQUIRED.
bool is_public_link = 2;
// OPTIONAL.
string authorization = 2;
string webdav_authorization = 3;
// OPTIONAL.
string reva_authorization = 4;
// OPTIONAL.
string public_link_token = 5;
}
message CS3Source {
string path = 1;
string authorization = 2;
}
// The service response
+142 -58
View File
@@ -11,8 +11,12 @@ import (
v0proto "github.com/owncloud/ocis/thumbnails/pkg/proto/v0"
"github.com/owncloud/ocis/thumbnails/pkg/thumbnail"
"github.com/owncloud/ocis/thumbnails/pkg/thumbnail/imgsource"
"github.com/pkg/errors"
"google.golang.org/grpc/metadata"
"image"
"net/url"
"path"
"strings"
)
// NewService returns a service implementation for Service.
@@ -53,82 +57,162 @@ type Thumbnail struct {
func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetThumbnailRequest, rsp *v0proto.GetThumbnailResponse) error {
_, ok := v0proto.GetThumbnailRequest_FileType_value[req.ThumbnailType.String()]
if !ok {
g.logger.Debug().Str("filetype", req.ThumbnailType.String()).Msg("unsupported filetype")
g.logger.Debug().Str("thumbnail_type", req.ThumbnailType.String()).Msg("unsupported thumbnail type")
return nil
}
encoder := thumbnail.EncoderForType(req.ThumbnailType.String())
if encoder == nil {
g.logger.Debug().Str("filetype", req.ThumbnailType.String()).Msg("unsupported filetype")
return nil
}
sReq := &provider.StatRequest{
Ref: &provider.Reference{
Spec: &provider.Reference_Path{Path: "/home/" + req.Filepath},
},
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return merrors.Unauthorized(g.serviceID, "authorization is missing")
}
auth, ok := md[token.TokenHeader]
if !ok {
return merrors.Unauthorized(g.serviceID, "authorization is missing")
}
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, auth[0])
sRes, err := g.cs3Client.Stat(ctx, sReq)
if err != nil {
g.logger.Error().Err(err).Msg("could stat file")
return merrors.InternalServerError(g.serviceID, "could not stat file: %s", err.Error())
}
if sRes.Status.Code != rpc.Code_CODE_OK {
g.logger.Error().Err(err).Msg("could not create Request")
return merrors.InternalServerError(g.serviceID, "could not stat file: %s", err.Error())
}
tr := thumbnail.Request{
Resolution: image.Rect(0, 0, int(req.Width), int(req.Height)),
Encoder: encoder,
ETag: sRes.GetInfo().GetChecksum().GetSum(),
}
thumb, ok := g.manager.Get(tr)
if ok {
rsp.Thumbnail = thumb
rsp.Mimetype = tr.Encoder.MimeType()
g.logger.Debug().Str("thumbnail_type", req.ThumbnailType.String()).Msg("unsupported thumbnail type")
return nil
}
var img image.Image
var thumb []byte
var err error
switch {
case req.GetWebdavSource() != nil:
src := req.GetWebdavSource()
src.GetAuthorization()
sCtx := imgsource.ContextSetAuthorization(ctx, src.GetAuthorization())
img, err = g.webdavSource.Get(sCtx, src.GetUrl())
thumb, err = g.handleWebdavSource(ctx, req, encoder)
case req.GetCs3Source() != nil:
src := req.GetCs3Source()
sCtx := imgsource.ContextSetAuthorization(ctx, auth[0])
img, err = g.cs3Source.Get(sCtx, src.Path)
thumb, err = g.handleCS3Source(ctx, req, encoder)
default:
g.logger.Error().Msg("no image source provided")
return merrors.BadRequest(g.serviceID, "image source is missing")
}
if err != nil {
return merrors.InternalServerError(g.serviceID, "could not get image from source: %v", err.Error())
}
if img == nil {
return merrors.InternalServerError(g.serviceID, "could not get image from source")
}
if thumb, err = g.manager.Generate(tr, img); err != nil {
return err
}
rsp.Thumbnail = thumb
rsp.Mimetype = tr.Encoder.MimeType()
rsp.Mimetype = encoder.MimeType()
return nil
}
func (g Thumbnail) handleCS3Source(ctx context.Context, req *v0proto.GetThumbnailRequest, encoder thumbnail.Encoder) ([]byte, error) {
src := req.GetCs3Source()
sRes, err := g.stat(src.Path, src.Authorization)
if err != nil {
return nil, err
}
tr := thumbnail.Request{
Resolution: image.Rect(0, 0, int(req.Width), int(req.Height)),
Encoder: encoder,
Checksum: sRes.GetInfo().GetChecksum().GetSum(),
}
thumb, ok := g.manager.Get(tr)
if ok {
return thumb, nil
}
ctx = imgsource.ContextSetAuthorization(ctx, src.Authorization)
img, err := g.cs3Source.Get(ctx, src.Path)
if err != nil {
return nil, merrors.InternalServerError(g.serviceID, "could not get image from source: %s", err.Error())
}
if img == nil {
return nil, merrors.InternalServerError(g.serviceID, "could not get image from source")
}
if thumb, err = g.manager.Generate(tr, img); err != nil {
return nil, err
}
return thumb, nil
}
func (g Thumbnail) handleWebdavSource(ctx context.Context, req *v0proto.GetThumbnailRequest, encoder thumbnail.Encoder) ([]byte, error) {
src := req.GetWebdavSource()
imgURL, err := url.Parse(src.Url)
if err != nil {
return nil, errors.Wrap(err, "source url is invalid")
}
var auth, statPath string
if src.IsPublicLink {
q := imgURL.Query()
var rsp *gateway.AuthenticateResponse
if q.Get("signature") != "" && q.Get("expiration") != "" {
// Handle pre-signed public links
sig := q.Get("signature")
exp := q.Get("expiration")
rsp, err = g.cs3Client.Authenticate(ctx, &gateway.AuthenticateRequest{
Type: "publicshares",
ClientId: src.PublicLinkToken,
ClientSecret: strings.Join([]string{"signature", sig, exp}, "|"),
})
} else {
rsp, err = g.cs3Client.Authenticate(ctx, &gateway.AuthenticateRequest{
Type: "publicshares",
ClientId: src.PublicLinkToken,
// We pass an empty password because we expect non pre-signed public links
// to not be password protected
ClientSecret: "password|",
})
}
if err != nil {
return nil, merrors.InternalServerError(g.serviceID, "could not authenticate: %s", err.Error())
}
auth = rsp.Token
statPath = path.Join("/public", src.PublicLinkToken, req.Filepath)
} else {
auth = src.RevaAuthorization
statPath = path.Join("/home", req.Filepath)
}
sRes, err := g.stat(statPath, auth)
if err != nil {
return nil, err
}
tr := thumbnail.Request{
Resolution: image.Rect(0, 0, int(req.Width), int(req.Height)),
Encoder: encoder,
Checksum: sRes.GetInfo().GetChecksum().GetSum(),
}
thumb, ok := g.manager.Get(tr)
if ok {
return thumb, nil
}
if src.WebdavAuthorization != "" {
ctx = imgsource.ContextSetAuthorization(ctx, src.WebdavAuthorization)
}
imgURL.RawQuery = ""
img, err := g.webdavSource.Get(ctx, imgURL.String())
if err != nil {
return nil, merrors.InternalServerError(g.serviceID, "could not get image from source: %s", err.Error())
}
if img == nil {
return nil, merrors.InternalServerError(g.serviceID, "could not get image from source")
}
if thumb, err = g.manager.Generate(tr, img); err != nil {
return nil, err
}
return thumb, nil
}
func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) {
ctx := metadata.AppendToOutgoingContext(context.Background(), token.TokenHeader, auth)
req := &provider.StatRequest{
Ref: &provider.Reference{
Spec: &provider.Reference_Path{Path: path},
},
}
rsp, err := g.cs3Client.Stat(ctx, req)
if err != nil {
g.logger.Error().Err(err).Str("path", path).Msg("could not stat file")
return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", err.Error())
}
if rsp.Status.Code != rpc.Code_CODE_OK {
g.logger.Error().Str("status_message", rsp.Status.Message).Str("path", path).Msg("could not stat file")
return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", rsp.Status.Message)
}
if rsp.Info.GetChecksum().GetSum() == "" {
g.logger.Error().Msg("resource info is missing checksum")
return nil, merrors.InternalServerError(g.serviceID, "resource info is missing a checksum")
}
return rsp, nil
}
+8 -10
View File
@@ -26,34 +26,32 @@ type WebDav struct {
}
// Get downloads the file from a webdav service
func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) {
req, err := http.NewRequest(http.MethodGet, file, nil)
func (s WebDav) Get(ctx context.Context, url string) (image.Image, error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, errors.Wrapf(err, `could not get the image "%s"`, file)
return nil, errors.Wrapf(err, `could not get the image "%s"`, url)
}
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: s.insecure} //nolint:gosec
auth, ok := ContextGetAuthorization(ctx)
if !ok {
return nil, fmt.Errorf("could not get image \"%s\" error: authorization is missing", file)
if auth, ok := ContextGetAuthorization(ctx); ok {
req.Header.Add("Authorization", auth)
}
req.Header.Add("Authorization", auth)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, errors.Wrapf(err, `could not get the image "%s"`, file)
return nil, errors.Wrapf(err, `could not get the image "%s"`, url)
}
defer resp.Body.Close() //nolint:errcheck
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("could not get the image \"%s\". Request returned with statuscode %d ", file, resp.StatusCode)
return nil, fmt.Errorf("could not get the image \"%s\". Request returned with statuscode %d ", url, resp.StatusCode)
}
img, _, err := image.Decode(resp.Body)
if err != nil {
return nil, errors.Wrapf(err, `could not decode the image "%s"`, file)
return nil, errors.Wrapf(err, `could not decode the image "%s"`, url)
}
return img, nil
}
-9
View File
@@ -93,15 +93,6 @@ func (rs Resolutions) ClosestMatch(requested image.Rectangle, sourceSize image.R
return match
}
func mapRatio(given image.Rectangle, other image.Rectangle) image.Rectangle {
isLandscape := given.Dx() > given.Dy()
ratio := float64(given.Dx()) / float64(given.Dy())
if isLandscape {
return image.Rect(0, 0, other.Dx(), int(float64(other.Dx())/ratio))
}
return image.Rect(0, 0, int(float64(other.Dy())*ratio), other.Dy())
}
func dimensionLength(rect image.Rectangle, isLandscape bool) int {
if isLandscape {
return rect.Dx()
@@ -119,20 +119,3 @@ func TestParseResolution(t *testing.T) {
t.Errorf("Expected resolution %s got %s", rStr, r.String())
}
}
func TestMapRatio(t *testing.T) {
testData := [][]image.Rectangle{
{image.Rect(0, 0, 1920, 1080), image.Rect(0, 0, 32, 32), image.Rect(0, 0, 32, 18)},
{image.Rect(0, 0, 1080, 1920), image.Rect(0, 0, 32, 32), image.Rect(0, 0, 18, 32)},
{image.Rect(0, 0, 1024, 735), image.Rect(0, 0, 32, 32), image.Rect(0, 0, 32, 22)},
}
for _, row := range testData {
given := row[0]
other := row[1]
expected := row[2]
mapped := mapRatio(given, other)
if mapped.Dx() != expected.Dx() || mapped.Dy() != expected.Dy() {
t.Errorf("Expected %dx%d got %dx%d", expected.Dx(), expected.Dy(), mapped.Dx(), mapped.Dy())
}
}
}
@@ -7,7 +7,6 @@ import (
"os"
"path/filepath"
"strconv"
"strings"
)
const (
@@ -67,20 +66,15 @@ func (s *FileSystem) Put(key string, img []byte) error {
// BuildKey generate the unique key for a thumbnail.
// The key is structure as follows:
//
// <first two letters of etag>/<next two letters of etag>/<rest of etag>/<width>x<height>.<filetype>
// <first two letters of checksum>/<next two letters of checksum>/<rest of checksum>/<width>x<height>.<filetype>
//
// e.g. 97/9f/4c8db98f7b82e768ef478d3c8612/500x300.png
//
// The key also represents the path to the thumbnail in the filesystem under the configured root directory.
func (s *FileSystem) BuildKey(r Request) string {
etag := r.ETag
checksum := r.Checksum
filetype := r.Types[0]
filename := strconv.Itoa(r.Resolution.Dx()) + "x" + strconv.Itoa(r.Resolution.Dy()) + "." + filetype
return filepath.Join(etag[:2], etag[2:4], etag[4:], filename)
}
func (s *FileSystem) rootDir(key string) string {
p := strings.Split(key, string(os.PathSeparator))
return p[0]
return filepath.Join(checksum[:2], checksum[2:4], checksum[4:], filename)
}
+1 -1
View File
@@ -31,7 +31,7 @@ func (s InMemory) Put(key string, thumbnail []byte) error {
// BuildKey generates a unique key to store and retrieve the thumbnail.
func (s InMemory) BuildKey(r Request) string {
parts := []string{
r.ETag,
r.Checksum,
r.Resolution.String(),
strings.Join(r.Types, ","),
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
// Request combines different attributes needed for storage operations.
type Request struct {
ETag string
Checksum string
Types []string
Resolution image.Rectangle
}
+13 -17
View File
@@ -2,9 +2,9 @@ package thumbnail
import (
"bytes"
"github.com/disintegration/imaging"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/thumbnails/pkg/thumbnail/storage"
"golang.org/x/image/draw"
"image"
)
@@ -12,14 +12,14 @@ import (
type Request struct {
Resolution image.Rectangle
Encoder Encoder
ETag string
Checksum string
}
// Manager is responsible for generating thumbnails
type Manager interface {
// Get will return a thumbnail for a file
// Generate will return a thumbnail for a file
Generate(Request, image.Image) ([]byte, error)
// GetStored loads the thumbnail from the storage.
// Get loads the thumbnail from the storage.
// It will return nil if no image is stored for the given context.
Get(Request) ([]byte, bool)
}
@@ -40,7 +40,7 @@ type SimpleManager struct {
resolutions Resolutions
}
// Get implements the Get Method of Manager
// Generate implements the Get Method of Manager
func (s SimpleManager) Generate(r Request, img image.Image) ([]byte, error) {
match := s.resolutions.ClosestMatch(r.Resolution, img.Bounds())
thumbnail := s.generate(match, img)
@@ -51,33 +51,29 @@ func (s SimpleManager) Generate(r Request, img image.Image) ([]byte, error) {
return nil, err
}
key := s.storage.BuildKey(mapToStorageRequest(r))
err = s.storage.Put(key, dst.Bytes())
k := s.storage.BuildKey(mapToStorageRequest(r))
err = s.storage.Put(k, dst.Bytes())
if err != nil {
s.logger.Warn().Err(err).Msg("could not store thumbnail")
}
return dst.Bytes(), nil
}
// GetStored tries to get the stored thumbnail and return it.
// Get tries to get the stored thumbnail and return it.
// If there is no cached thumbnail it will return nil
func (s SimpleManager) Get(r Request) ([]byte, bool) {
key := s.storage.BuildKey(mapToStorageRequest(r))
return s.storage.Get(key)
k := s.storage.BuildKey(mapToStorageRequest(r))
return s.storage.Get(k)
}
func (s SimpleManager) generate(r image.Rectangle, img image.Image) image.Image {
targetResolution := mapRatio(img.Bounds(), r)
thumbnail := image.NewRGBA(targetResolution)
draw.ApproxBiLinear.Scale(thumbnail, targetResolution, img, img.Bounds(), draw.Over, nil)
return thumbnail
return imaging.Thumbnail(img, r.Dx(), r.Dy(), imaging.Lanczos)
}
func mapToStorageRequest(r Request) storage.Request {
sR := storage.Request{
ETag: r.ETag,
return storage.Request{
Checksum: r.Checksum,
Resolution: r.Resolution,
Types: r.Encoder.Types(),
}
return sR
}
+2 -2
View File
@@ -33,7 +33,7 @@ func BenchmarkGet(b *testing.B) {
res, _ := ParseResolution("32x32")
req := Request{
Resolution: res,
ETag: "1872ade88f3013edeb33decd74a4f947",
Checksum: "1872ade88f3013edeb33decd74a4f947",
}
cwd, _ := os.Getwd()
p := filepath.Join(cwd, "../../testdata/oc.png")
@@ -42,6 +42,6 @@ func BenchmarkGet(b *testing.B) {
img, ext, _ := image.Decode(f)
req.Encoder = EncoderForType(ext)
for i := 0; i < b.N; i++ {
_, _ = sut.Get(req, img)
_, _ = sut.Generate(req, img)
}
}