felt/register/register.go

119 lines
3.2 KiB
Go

package register
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"html/template"
"net/http"
"time"
"strconv"
"fmt"
"hacklab.nilfm.cc/quartzgun/auth"
"hacklab.nilfm.cc/quartzgun/renderer"
"hacklab.nilfm.cc/quartzgun/router"
"hacklab.nilfm.cc/quartzgun/util"
)
type SymmetricCrypto interface {
Encode(b []byte) string
Decode(s string) []byte
Encrypt(text string) (string, error)
Decrypt(text string) (string, error)
IsValid(text string) bool
}
type SymmetricCrypt struct {
Secret string
}
var iv []byte = []byte {107, 53, 46, 249, 52, 70, 36, 185,
168, 139, 144, 249, 242, 2, 125, 183 }
func (self *SymmetricCrypt) IsValid(cipher string) bool {
stringTimestamp, err := self.Decrypt(cipher)
if err != nil {
return false
}
int64Timestamp, err := strconv.ParseInt(stringTimestamp, 10, 64)
if err != nil {
return false
}
then := time.UnixMicro(int64Timestamp)
return time.Since(then).Minutes() <= 15
}
func (self *SymmetricCrypt) Encode(b []byte) string {
return hex.EncodeToString(b)
}
func (self *SymmetricCrypt) Decode(s string) []byte {
data, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return data
}
func (self *SymmetricCrypt) Encrypt(text string) (string, error) {
fmt.Println(text)
block, err := aes.NewCipher([]byte(self.Secret))
if err != nil {
return "", err
}
plainText := []byte(text)
cfb := cipher.NewCFBEncrypter(block, iv)
cipherText := make([]byte, len(plainText))
cfb.XORKeyStream(cipherText, plainText)
return self.Encode(cipherText), nil
}
func (self *SymmetricCrypt) Decrypt(text string) (string, error) {
block, err := aes.NewCipher([]byte(self.Secret))
if err != nil {
return "", err
}
cipherText := self.Decode(text)
cfb := cipher.NewCFBDecrypter(block, iv)
plainText := make([]byte, len(cipherText))
cfb.XORKeyStream(plainText, cipherText)
return string(plainText), nil
}
func WithCrypto(next http.Handler, crypto SymmetricCrypto) http.Handler {
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
util.AddContextValue(req, "crypto", crypto);
next.ServeHTTP(w, req)
}
return http.HandlerFunc(handlerFunc)
}
func WithUserStoreAndCrypto(next http.Handler, udb auth.UserStore, crypto SymmetricCrypto) http.Handler {
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
urlParams := req.Context().Value("params").(map[string]string)
success := false
cipher := urlParams["cipher"]
username := req.FormValue("username")
password := req.FormValue("password")
if crypto.IsValid(cipher) && len(username) > 0 && len(password) > 0 {
success = udb.AddUser(username, password) == nil
}
util.AddContextValue(req, "success", success);
next.ServeHTTP(w, req)
}
return http.HandlerFunc(handlerFunc)
}
func CreateRegistrationInterface(udb auth.UserStore, secret string) http.Handler {
rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("templates/error.html"))}
crypto := &SymmetricCrypt{Secret: secret}
rtr.Get(`/(?P<cipher>\S+)`, WithCrypto(renderer.Template("templates/register.html"), crypto))
rtr.Post(`/(?P<cipher>\S+)`, WithUserStoreAndCrypto(renderer.Template("templates/registered.html"), udb, crypto))
return http.HandlerFunc(rtr.ServeHTTP)
}