Passworded pastes are here.
This commit is contained in:
parent
b8e9895617
commit
94a0a435b6
@ -1,5 +1,5 @@
|
||||
// Code generated by fileb0x at "2018-05-01 23:06:19.605611358 +0500 +05 m=+0.029101680" from config file "fileb0x.yml" DO NOT EDIT.
|
||||
// modification hash(397d57216a6c1e49feba398620b3b1d5.e30269e2254cb92c4231bb47f2cedf3a)
|
||||
// Code generated by fileb0x at "2018-05-18 22:25:13.180720675 +0500 +05 m=+0.030971779" from config file "fileb0x.yml" DO NOT EDIT.
|
||||
// modification hash(1679c5529785a2e624071b2679319ecb.608888160ddb195ed89fce5ab290ff92)
|
||||
|
||||
package static
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
35
api/http/static/b0xfile__passworded_paste_verify.html.go
Normal file
35
api/http/static/b0xfile__passworded_paste_verify.html.go
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
// Code generaTed by fileb0x at "2018-05-01 18:35:23.707239688 +0500 +05 m=+0.052534784" from config file "fileb0x.yml" DO NOT EDIT.
|
||||
// modified(2018-05-01 18:35:19.361392967 +0500 +05)
|
||||
// Code generaTed by fileb0x at "2018-05-18 22:25:13.191256649 +0500 +05 m=+0.041507879" from config file "fileb0x.yml" DO NOT EDIT.
|
||||
// modified(2018-05-18 22:17:55.447214662 +0500 +05)
|
||||
// original path: assets/pastelist_list.html
|
||||
|
||||
package static
|
||||
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// FilePastelistListHTML is "/pastelist_list.html"
|
||||
var FilePastelistListHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x20\x2d\x20\x45\x52\x52\x4f\x52\x21\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\x20\x64\x65\x66\x65\x72\x20\x73\x72\x63\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x6a\x73\x2f\x66\x6f\x6e\x74\x61\x77\x65\x73\x6f\x6d\x65\x2d\x35\x2e\x30\x2e\x37\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x75\x72\x67\x65\x72\x20\x62\x75\x72\x67\x65\x72\x22\x20\x64\x61\x74\x61\x2d\x74\x61\x72\x67\x65\x74\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x20\x20\x20\x20\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x73\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e")
|
||||
var FilePastelistListHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x20\x2d\x20\x50\x61\x73\x74\x65\x73\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\x20\x64\x65\x66\x65\x72\x20\x73\x72\x63\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x6a\x73\x2f\x66\x6f\x6e\x74\x61\x77\x65\x73\x6f\x6d\x65\x2d\x35\x2e\x30\x2e\x37\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x75\x72\x67\x65\x72\x20\x62\x75\x72\x67\x65\x72\x22\x20\x64\x61\x74\x61\x2d\x74\x61\x72\x67\x65\x74\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x20\x20\x20\x20\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x73\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e")
|
||||
|
||||
func init() {
|
||||
|
||||
|
@ -73,11 +73,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="Should this paste be accessible only with special URL and not shown in pastes list?">
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="Should this paste be accessible only with special URL and not shown in pastes list? WARNING: If you'll enter password into 'Password for paste' field this checkbox will be assumed as checked!">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="paste-private" id="paste-private"> Private paste with unique URL?
|
||||
</label>
|
||||
</div>
|
||||
<div>OR</div>
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private
|
||||
paste with unique URL' checkbox will be assumed as checked.">
|
||||
<label for="paste-password">Password for paste:</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="paste-language">Language:</label>
|
||||
<div class="control">
|
||||
|
69
assets/passworded_paste_verify.html
Normal file
69
assets/passworded_paste_verify.html
Normal file
@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fast Paste Bin - Paste is protected with password</title>
|
||||
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
|
||||
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar is-dark">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">Fast Paste Bin</a>
|
||||
<a class="navbar-item" href="/pastes/">
|
||||
Pastes
|
||||
</a>
|
||||
<div class="navbar-burger burger" data-target="navbarItems">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="navbarItems" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="section">
|
||||
<div class="content">
|
||||
<div class="columns">
|
||||
<div class="column is-4 is-offset-4">
|
||||
<form action="/paste/{pasteID}/{pasteTimestamp}/verify" method="POST" autocomplete="off">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
This paste is protected with password
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private paste with unique URL' checkbox will be assumed as checked.">
|
||||
<label for="paste-password">Password for paste:</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="button is-success" type="submit" value="Check password!">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fast Paste Bin - ERROR!</title>
|
||||
<title>Fast Paste Bin - Paste #{pasteID}</title>
|
||||
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
|
||||
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
@ -41,6 +41,7 @@
|
||||
<th>Title</th>
|
||||
<th>Language</th>
|
||||
<th>Pasted on</th>
|
||||
<th>Will expire on</th>
|
||||
<th>Paste type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -50,10 +51,11 @@
|
||||
<td>{pasteTitle}</td>
|
||||
<td>{pasteLanguage}</td>
|
||||
<td>{pasteDate}</td>
|
||||
<td>{pasteExpiration}</td>
|
||||
<td>{pasteType}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<td colspan="6">
|
||||
<a class="button" href="/paste/{pasteID}/{pasteTs}raw">View raw</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fast Paste Bin - ERROR!</title>
|
||||
<title>Fast Paste Bin - Pastes</title>
|
||||
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
|
||||
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
58
database/migrations/4_passworded_pastes.go
Normal file
58
database/migrations/4_passworded_pastes.go
Normal file
@ -0,0 +1,58 @@
|
||||
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||
//
|
||||
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||
// developers.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject
|
||||
// to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
// stdlib
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
func PasswordedPastesUp(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE `pastes` ADD `password` varchar(64) NOT NULL DEFAULT '' COMMENT 'Password for paste (scrypted and sha256ed).'")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err1 := tx.Exec("ALTER TABLE `pastes` ADD `password_salt` varchar(64) NOT NULL DEFAULT '' COMMENT 'Password salt (sha256ed).'")
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func PasswordedPastesDown(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `password`")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err1 := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `password_salt`")
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -100,6 +100,7 @@ custom:
|
||||
- "assets/pagination_link_current.html"
|
||||
- "assets/pagination_link.html"
|
||||
- "assets/pagination.html"
|
||||
- "assets/passworded_paste_verify.html"
|
||||
- "assets/paste.html"
|
||||
- "assets/pastelist_list.html"
|
||||
- "assets/pastelist_paste.html"
|
||||
|
@ -27,7 +27,6 @@ package pastes
|
||||
import (
|
||||
// stdlib
|
||||
"bytes"
|
||||
//"html"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -97,6 +96,27 @@ func pasteGET(ec echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
if paste.Private && paste.Password != "" {
|
||||
// Check if cookie for this paste is defined. This means that user
|
||||
// previously successfully entered a password.
|
||||
cookie, err := ec.Cookie("PASTE-" + strconv.Itoa(pasteID))
|
||||
if err != nil {
|
||||
// No cookie, redirect to auth page.
|
||||
c.Logger.Info().Msg("Tried to access passworded paste without autorization, redirecting to auth page...")
|
||||
return ec.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ec.Param("timestamp")+"/verify")
|
||||
}
|
||||
|
||||
// Generate cookie value to check.
|
||||
cookieValue := paste.GenerateCryptedCookieValue()
|
||||
|
||||
if cookieValue != cookie.Value {
|
||||
c.Logger.Info().Msg("Invalid cookie, redirecting to auth page...")
|
||||
return ec.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ec.Param("timestamp")+"/verify")
|
||||
}
|
||||
|
||||
// If all okay - do nothing :)
|
||||
}
|
||||
|
||||
pasteHTML, err2 := static.ReadFile("paste.html")
|
||||
if err2 != nil {
|
||||
return ec.String(http.StatusNotFound, "parse.html wasn't found!")
|
||||
@ -106,6 +126,7 @@ func pasteGET(ec echo.Context) error {
|
||||
pasteHTMLAsString := strings.Replace(string(pasteHTML), "{pasteTitle}", paste.Title, 1)
|
||||
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteID}", strconv.Itoa(paste.ID), -1)
|
||||
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteDate}", paste.CreatedAt.Format("2006-01-02 @ 15:04:05"), 1)
|
||||
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteExpiration}", paste.GetExpirationTime().Format("2006-01-02 @ 15:04:05"), 1)
|
||||
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteLanguage}", paste.Language, 1)
|
||||
|
||||
if paste.Private {
|
||||
@ -151,6 +172,99 @@ func pasteGET(ec echo.Context) error {
|
||||
return ec.HTML(http.StatusOK, string(pasteHTMLAsString))
|
||||
}
|
||||
|
||||
// GET for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
|
||||
func pastePasswordedVerifyGet(ec echo.Context) error {
|
||||
verifyHTMLRaw, err := static.ReadFile("passworded_paste_verify.html")
|
||||
if err != nil {
|
||||
return ec.String(http.StatusNotFound, "passworded_paste_verify.html wasn't found!")
|
||||
}
|
||||
|
||||
errhtml, err := static.ReadFile("error.html")
|
||||
if err != nil {
|
||||
return ec.String(http.StatusNotFound, "error.html wasn't found!")
|
||||
}
|
||||
|
||||
pasteIDRaw := ec.Param("id")
|
||||
timestampRaw := ec.Param("timestamp")
|
||||
// We already get numbers from string, so we will not check strconv.Atoi()
|
||||
// error.
|
||||
pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
|
||||
|
||||
// Get paste.
|
||||
paste, err1 := GetByID(pasteID)
|
||||
if err1 != nil {
|
||||
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
|
||||
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
|
||||
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
|
||||
}
|
||||
|
||||
// Check for auth cookie. If present - redirect to paste.
|
||||
cookie, err := ec.Cookie("PASTE-" + strconv.Itoa(pasteID))
|
||||
if err == nil {
|
||||
// No cookie, redirect to auth page.
|
||||
c.Logger.Debug().Msg("Paste cookie found, checking it...")
|
||||
|
||||
// Generate cookie value to check.
|
||||
cookieValue := paste.GenerateCryptedCookieValue()
|
||||
|
||||
if cookieValue == cookie.Value {
|
||||
c.Logger.Info().Msg("Valid cookie, redirecting to paste page...")
|
||||
return ec.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ec.Param("timestamp"))
|
||||
}
|
||||
|
||||
c.Logger.Debug().Msg("Invalid cookie, showing auth page")
|
||||
}
|
||||
|
||||
verifyHTML := strings.Replace(string(verifyHTMLRaw), "{pasteID}", strconv.Itoa(pasteID), -1)
|
||||
verifyHTML = strings.Replace(verifyHTML, "{pasteTimestamp}", timestampRaw, 1)
|
||||
|
||||
return ec.HTML(http.StatusOK, verifyHTML)
|
||||
}
|
||||
|
||||
// POST for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
|
||||
func pastePasswordedVerifyPost(ec echo.Context) error {
|
||||
errhtml, err := static.ReadFile("error.html")
|
||||
if err != nil {
|
||||
return ec.String(http.StatusNotFound, "error.html wasn't found!")
|
||||
}
|
||||
|
||||
pasteIDRaw := ec.Param("id")
|
||||
timestampRaw := ec.Param("timestamp")
|
||||
// We already get numbers from string, so we will not check strconv.Atoi()
|
||||
// error.
|
||||
pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
|
||||
c.Logger.Debug().Msgf("Requesting paste #%+v", pasteID)
|
||||
|
||||
// Get paste.
|
||||
paste, err1 := GetByID(pasteID)
|
||||
if err1 != nil {
|
||||
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
|
||||
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
|
||||
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
|
||||
}
|
||||
|
||||
params, err2 := ec.FormParams()
|
||||
if err2 != nil {
|
||||
c.Logger.Debug().Msg("No form parameters passed")
|
||||
return ec.HTML(http.StatusBadRequest, string(errhtml))
|
||||
}
|
||||
|
||||
if paste.VerifyPassword(params["paste-password"][0]) {
|
||||
// Set cookie that this paste's password is verified and paste
|
||||
// can be viewed.
|
||||
cookie := new(http.Cookie)
|
||||
cookie.Name = "PASTE-" + strconv.Itoa(pasteID)
|
||||
cookie.Value = paste.GenerateCryptedCookieValue()
|
||||
cookie.Expires = time.Now().Add(24 * time.Hour)
|
||||
ec.SetCookie(cookie)
|
||||
|
||||
return ec.Redirect(http.StatusFound, "/paste/"+strconv.Itoa(pasteID)+"/"+timestampRaw)
|
||||
}
|
||||
|
||||
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid password. Please, try again.", 1)
|
||||
return ec.HTML(http.StatusBadRequest, string(errhtmlAsString))
|
||||
}
|
||||
|
||||
// POST for "/paste/" which will create new paste and redirect to
|
||||
// "/pastes/CREATED_PASTE_ID".
|
||||
func pastePOST(ec echo.Context) error {
|
||||
@ -227,10 +341,15 @@ func pastePOST(ec echo.Context) error {
|
||||
// Private paste?
|
||||
paste.Private = false
|
||||
privateCheckbox, privateCheckboxFound := params["paste-private"]
|
||||
if privateCheckboxFound && privateCheckbox[0] == "on" {
|
||||
pastePassword, pastePasswordFound := params["paste-password"]
|
||||
if privateCheckboxFound && privateCheckbox[0] == "on" || pastePasswordFound && len(pastePassword[0]) != 0 {
|
||||
paste.Private = true
|
||||
}
|
||||
|
||||
if len(pastePassword) != 0 {
|
||||
paste.CreatePassword(pastePassword[0])
|
||||
}
|
||||
|
||||
id, err2 := Save(paste)
|
||||
if err2 != nil {
|
||||
c.Logger.Debug().Msgf("Failed to save paste: %s", err2.Error())
|
||||
@ -284,6 +403,13 @@ func pasteRawGET(ec echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// ToDo: figure out how to handle passworded pastes here.
|
||||
// Return error for now.
|
||||
if paste.Password != "" {
|
||||
c.Logger.Error().Msgf("Cannot render paste #%d as raw: passworded paste. Patches welcome!", pasteID)
|
||||
return ec.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
|
||||
}
|
||||
|
||||
return ec.String(http.StatusOK, paste.Data)
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,9 @@ func New(cc *context.Context) {
|
||||
c.Echo.GET("/paste/:id/:timestamp", pasteGET)
|
||||
// Show RAW representation of private paste.
|
||||
c.Echo.GET("/paste/:id/:timestamp/raw", pasteRawGET)
|
||||
// Verify access to passworded paste.
|
||||
c.Echo.GET("/paste/:id/:timestamp/verify", pastePasswordedVerifyGet)
|
||||
c.Echo.POST("/paste/:id/:timestamp/verify", pastePasswordedVerifyPost)
|
||||
|
||||
// Pastes list.
|
||||
c.Echo.GET("/pastes/", pastesGET)
|
||||
|
@ -26,9 +26,13 @@ package pastes
|
||||
|
||||
import (
|
||||
// stdlib
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
// other
|
||||
//"github.com/alecthomas/chroma"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -36,6 +40,8 @@ const (
|
||||
PASTE_KEEP_FOR_HOURS = 2
|
||||
PASTE_KEEP_FOR_DAYS = 3
|
||||
PASTE_KEEP_FOR_MONTHS = 4
|
||||
|
||||
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -61,6 +67,35 @@ type Paste struct {
|
||||
PasswordSalt string `db:"password_salt"`
|
||||
}
|
||||
|
||||
// CreatePassword creates password for current paste.
|
||||
func (p *Paste) CreatePassword(password string) error {
|
||||
// Create salt - random string.
|
||||
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
saltBytes := make([]byte, 64)
|
||||
for i := range saltBytes {
|
||||
saltBytes[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
|
||||
saltHashBytes := sha256.Sum256(saltBytes)
|
||||
p.PasswordSalt = fmt.Sprintf("%x", saltHashBytes)
|
||||
|
||||
// Create crypted password and hash it.
|
||||
passwordCrypted, err := scrypt.Key([]byte(password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordHashBytes := sha256.Sum256(passwordCrypted)
|
||||
p.Password = fmt.Sprintf("%x", passwordHashBytes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateCryptedCookieValue generates crypted cookie value for paste.
|
||||
func (p *Paste) GenerateCryptedCookieValue() string {
|
||||
cookieValueCrypted, _ := scrypt.Key([]byte(p.Password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||
return fmt.Sprintf("%x", sha256.Sum256(cookieValueCrypted))
|
||||
}
|
||||
|
||||
func (p *Paste) GetExpirationTime() time.Time {
|
||||
var expirationTime time.Time
|
||||
switch p.KeepForUnitType {
|
||||
@ -88,3 +123,20 @@ func (p *Paste) IsExpired() bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// VerifyPassword verifies that provided password is valid.
|
||||
func (p *Paste) VerifyPassword(password string) bool {
|
||||
// Create crypted password and hash it.
|
||||
passwordCrypted, err := scrypt.Key([]byte(password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
passwordHashBytes := sha256.Sum256(passwordCrypted)
|
||||
providedPassword := fmt.Sprintf("%x", passwordHashBytes)
|
||||
|
||||
if providedPassword == p.Password {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ func GetPastesPages() int {
|
||||
// Save saves paste to database and returns it's ID.
|
||||
func Save(p *Paste) (int64, error) {
|
||||
dbConn := c.Database.GetDatabaseConnection()
|
||||
result, err := dbConn.NamedExec("INSERT INTO `pastes` (title, data, created_at, keep_for, keep_for_unit_type, language, private) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language, :private)", p)
|
||||
result, err := dbConn.NamedExec("INSERT INTO `pastes` (title, data, created_at, keep_for, keep_for_unit_type, language, private, password, password_salt) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language, :private, :password, :password_salt)", p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user