Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
P
perxis-go
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Package Registry
Operate
Terraform modules
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
perxis
perxis-go
Commits
d78a5294
Commit
d78a5294
authored
2 years ago
by
ko_oler
Browse files
Options
Downloads
Patches
Plain Diff
добавлен field и urlsigner
parent
fa821ebd
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
pkg/files/field.go
+156
-0
156 additions, 0 deletions
pkg/files/field.go
pkg/urlsigner/urlsigner.go
+126
-0
126 additions, 0 deletions
pkg/urlsigner/urlsigner.go
pkg/urlsigner/urlsigner_test.go
+92
-0
92 additions, 0 deletions
pkg/urlsigner/urlsigner_test.go
with
374 additions
and
0 deletions
pkg/files/field.go
0 → 100644
+
156
−
0
View file @
d78a5294
package
files
import
(
"context"
"errors"
"fmt"
"net/url"
"reflect"
"git.perx.ru/perxis/perxis-go/pkg/items"
"git.perx.ru/perxis/perxis-go/pkg/schema/field"
signer
"git.perx.ru/perxis/perxis-go/pkg/urlsigner"
"github.com/mitchellh/mapstructure"
)
const
FileTypeName
=
"file"
type
FileParameters
struct
{
t
*
FileType
}
func
(
p
FileParameters
)
Type
()
field
.
Type
{
return
p
.
t
}
func
(
p
*
FileParameters
)
Clone
(
reset
bool
)
field
.
Parameters
{
return
p
}
type
FileType
struct
{
fs
Files
signer
signer
.
URLSigner
fileServerUrl
string
uploader
Uploader
}
func
NewFileType
(
fs
Files
,
signer
signer
.
URLSigner
,
fileServerUrl
string
)
*
FileType
{
return
&
FileType
{
fs
:
fs
,
signer
:
signer
,
fileServerUrl
:
fileServerUrl
,
uploader
:
NewUploader
()}
}
func
(
t
*
FileType
)
WithUploader
(
uploader
Uploader
)
*
FileType
{
t
.
uploader
=
uploader
return
t
}
func
(
t
FileType
)
Name
()
string
{
return
FileTypeName
}
func
(
t
*
FileType
)
NewParameters
()
field
.
Parameters
{
return
&
FileParameters
{
t
}
}
func
(
t
FileType
)
Decode
(
_
context
.
Context
,
fld
*
field
.
Field
,
v
interface
{})
(
interface
{},
error
)
{
if
v
==
nil
{
return
nil
,
nil
}
var
f
File
if
err
:=
mapstructure
.
Decode
(
v
,
&
f
);
err
!=
nil
{
return
nil
,
err
}
return
&
f
,
nil
}
func
(
t
FileType
)
Encode
(
ctx
context
.
Context
,
fld
*
field
.
Field
,
v
interface
{})
(
interface
{},
error
)
{
if
v
==
nil
{
return
nil
,
nil
}
f
,
ok
:=
v
.
(
*
File
)
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"FileField encode error: incorrect type:
\"
%s
\"
, expected
\"
file
\"
"
,
reflect
.
ValueOf
(
v
)
.
Kind
())
}
if
f
.
File
!=
nil
{
// upload from file system
upload
,
err
:=
t
.
fs
.
Upload
(
ctx
,
f
)
if
err
!=
nil
{
return
nil
,
err
}
if
err
=
t
.
uploader
.
Upload
(
f
.
File
,
upload
);
err
!=
nil
{
return
nil
,
err
}
f
=
&
upload
.
File
}
u
:=
fmt
.
Sprintf
(
"%s/%s"
,
t
.
fileServerUrl
,
f
.
ID
)
resURL
,
err
:=
url
.
Parse
(
u
)
if
err
!=
nil
{
return
nil
,
err
}
if
t
.
signer
!=
nil
{
f
.
URL
=
t
.
signer
.
Sign
(
resURL
)
.
String
()
}
else
{
f
.
URL
=
resURL
.
String
()
}
res
:=
make
(
map
[
string
]
interface
{})
if
err
:=
mapstructure
.
Decode
(
f
,
&
res
);
err
!=
nil
{
return
nil
,
err
}
return
res
,
nil
}
// PreSave - функция буде вызвана перед сохранением поля в Storage. Реализует интерфейс `perxis.PreSaver`
// Выполняет проверку поля является ли файл только что загруженным и переносит его при необходимости для
// постоянного хранения
func
(
t
FileType
)
PreSave
(
ctx
context
.
Context
,
fld
*
field
.
Field
,
v
interface
{},
itemCtx
*
items
.
Context
)
(
interface
{},
bool
,
error
)
{
if
v
==
nil
{
return
nil
,
false
,
nil
}
f
:=
v
.
(
*
File
)
if
f
.
ID
==
""
{
return
nil
,
false
,
errors
.
New
(
"FileType: file id required"
)
}
if
!
f
.
Temporary
()
{
return
f
,
false
,
nil
}
f
,
err
:=
t
.
fs
.
MoveUpload
(
ctx
,
&
MultipartUpload
{
File
:
*
f
})
if
err
!=
nil
{
return
nil
,
false
,
err
}
return
f
,
true
,
nil
}
// Field - создает новое поле Field типа FileType
// FileType должен быть предварительно создан `NewFileType` и зарегистрирован `field.Register`
func
Field
(
o
...
interface
{})
*
field
.
Field
{
t
,
ok
:=
field
.
GetType
(
FileTypeName
)
if
!
ok
{
panic
(
"field file type not registered"
)
}
return
field
.
NewField
(
t
.
NewParameters
(),
o
...
)
}
func
(
t
*
FileType
)
IsEmpty
(
v
interface
{})
bool
{
if
v
==
nil
{
return
true
}
f
,
ok
:=
v
.
(
*
File
)
return
!
ok
||
f
.
ID
==
""
}
func
(
p
FileParameters
)
GetField
(
path
string
)
(
fld
*
field
.
Field
)
{
switch
path
{
case
"id"
,
"name"
,
"mimeType"
,
"url"
,
"key"
:
return
field
.
String
()
case
"size"
:
return
field
.
Number
(
field
.
NumberFormatInt
)
default
:
return
nil
}
}
func
init
()
{
// По умолчанию без FS
// Если нужны подписанные URL, и загрузка на FS, нужно зарегистрировать корректный типа
// См. cmd/content/command/server.go:195
field
.
Register
(
NewFileType
(
nil
,
nil
,
""
))
}
This diff is collapsed.
Click to expand it.
pkg/urlsigner/urlsigner.go
0 → 100644
+
126
−
0
View file @
d78a5294
package
urlsigner
import
(
"bytes"
"crypto/sha256"
"encoding/base64"
"net/url"
"strconv"
"strings"
"time"
"git.perx.ru/perxis/perxis-go/pkg/data"
)
type
URLSigner
interface
{
Sign
(
url
*
url
.
URL
)
*
url
.
URL
Check
(
url
*
url
.
URL
)
bool
}
const
(
defaultSignatureExpire
=
15
*
time
.
Minute
defaultQueryKey
=
"sign"
separator
=
"|"
saltSize
=
16
)
type
urlSigner
struct
{
secret
string
expirationTime
time
.
Duration
queryKey
string
params
[]
string
}
func
NewURLSigner
(
secret
string
,
expirationTime
time
.
Duration
,
queryKey
string
,
params
...
string
)
URLSigner
{
if
len
(
params
)
==
0
{
params
=
make
([]
string
,
0
)
}
if
expirationTime
==
0
{
expirationTime
=
defaultSignatureExpire
}
if
queryKey
==
""
{
queryKey
=
defaultQueryKey
}
return
&
urlSigner
{
secret
:
secret
,
expirationTime
:
expirationTime
,
queryKey
:
queryKey
,
params
:
params
,
}
}
func
(
s
*
urlSigner
)
Sign
(
u
*
url
.
URL
)
*
url
.
URL
{
q
:=
u
.
Query
()
h
:=
sha256
.
New
()
salt
:=
data
.
GenerateRandomString
(
saltSize
)
for
_
,
p
:=
range
s
.
params
{
if
vv
:=
q
[
p
];
len
(
vv
)
>
0
{
for
_
,
v
:=
range
vv
{
h
.
Write
([]
byte
(
v
))
}
}
}
h
.
Write
([]
byte
(
u
.
Path
))
h
.
Write
([]
byte
(
s
.
expirationTime
.
String
()))
h
.
Write
([]
byte
(
s
.
secret
))
h
.
Write
([]
byte
(
salt
))
expTime
:=
time
.
Now
()
.
Add
(
s
.
expirationTime
)
.
Unix
()
res
:=
strings
.
Join
([]
string
{
strconv
.
FormatInt
(
expTime
,
16
),
salt
,
string
(
h
.
Sum
(
nil
))},
separator
)
q
.
Set
(
s
.
queryKey
,
base64
.
URLEncoding
.
EncodeToString
([]
byte
(
res
)))
u
.
RawQuery
=
q
.
Encode
()
return
u
}
func
(
s
*
urlSigner
)
Check
(
u
*
url
.
URL
)
bool
{
q
:=
u
.
Query
()
sign
:=
q
.
Get
(
s
.
queryKey
)
if
sign
==
""
{
return
false
}
b
,
err
:=
base64
.
URLEncoding
.
DecodeString
(
sign
)
if
err
!=
nil
{
return
false
}
m
:=
bytes
.
Split
(
b
,
[]
byte
(
separator
))
if
len
(
m
)
<
3
{
return
false
}
expTime
,
err
:=
strconv
.
ParseInt
(
string
(
m
[
0
]),
16
,
64
)
if
err
!=
nil
||
time
.
Now
()
.
Unix
()
>
expTime
{
return
false
}
salt
:=
m
[
1
]
var
hash
[]
byte
for
i
:=
2
;
i
<
len
(
m
);
i
++
{
hash
=
append
(
hash
,
m
[
i
]
...
)
if
len
(
m
)
>
i
+
1
{
hash
=
append
(
hash
,
[]
byte
(
separator
)
...
)
}
}
h
:=
sha256
.
New
()
for
_
,
p
:=
range
s
.
params
{
if
vv
:=
q
[
p
];
len
(
vv
)
>
0
{
for
_
,
v
:=
range
vv
{
h
.
Write
([]
byte
(
v
))
}
}
}
h
.
Write
([]
byte
(
u
.
Path
))
h
.
Write
([]
byte
(
s
.
expirationTime
.
String
()))
h
.
Write
([]
byte
(
s
.
secret
))
h
.
Write
(
salt
)
return
bytes
.
Equal
(
hash
,
h
.
Sum
(
nil
))
}
This diff is collapsed.
Click to expand it.
pkg/urlsigner/urlsigner_test.go
0 → 100644
+
92
−
0
View file @
d78a5294
package
urlsigner
import
(
"net/url"
"testing"
"time"
"github.com/stretchr/testify/require"
)
const
secret
=
"secret_key"
func
TestSigner
(
t
*
testing
.
T
)
{
t
.
Run
(
"Not Signed"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
)
require
.
False
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"Simple"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
)
u
=
signer
.
Sign
(
u
)
require
.
True
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"Custom Query Key"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
"custom"
)
u
=
signer
.
Sign
(
u
)
require
.
True
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"URL Expired"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Millisecond
,
""
)
u
=
signer
.
Sign
(
u
)
time
.
Sleep
(
time
.
Second
)
require
.
False
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"Required Params"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"Correct"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path?param1=1¶m2=2"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
,
"param1"
,
"param2"
)
u
=
signer
.
Sign
(
u
)
require
.
True
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"One Param Empty"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path?param1=1"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
,
"param1"
,
"param2"
)
u
=
signer
.
Sign
(
u
)
require
.
True
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"Exchanged Values"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path?param1=1¶m2=2"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
,
"param1"
,
"param2"
)
u
=
signer
.
Sign
(
u
)
q
:=
u
.
Query
()
q
.
Set
(
"param1"
,
"2"
)
q
.
Set
(
"param2"
,
"1"
)
u
.
RawQuery
=
q
.
Encode
()
require
.
False
(
t
,
signer
.
Check
(
u
))
})
})
t
.
Run
(
"Extra params"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path?param1=1"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
,
"param1"
)
u
=
signer
.
Sign
(
u
)
q
:=
u
.
Query
()
q
.
Set
(
"extra"
,
"100"
)
u
.
RawQuery
=
q
.
Encode
()
require
.
True
(
t
,
signer
.
Check
(
u
))
})
t
.
Run
(
"Array Param Value"
,
func
(
t
*
testing
.
T
)
{
u
,
_
:=
url
.
Parse
(
"http://example.com/path?param1=1¶m1=2¶m1=3"
)
signer
:=
NewURLSigner
(
secret
,
time
.
Minute
,
""
,
"param1"
)
u
=
signer
.
Sign
(
u
)
q
:=
u
.
Query
()
q
.
Set
(
"param1"
,
"1"
)
u
.
RawQuery
=
q
.
Encode
()
require
.
False
(
t
,
signer
.
Check
(
u
))
})
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment