119 lines
3.3 KiB
Go
119 lines
3.3 KiB
Go
package register
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"encoding/hex"
|
|
"html/template"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"forge.lightcrystal.systems/nilix/quartzgun/auth"
|
|
. "forge.lightcrystal.systems/nilix/quartzgun/middleware"
|
|
"forge.lightcrystal.systems/nilix/quartzgun/rateLimiter"
|
|
"forge.lightcrystal.systems/nilix/quartzgun/renderer"
|
|
"forge.lightcrystal.systems/nilix/quartzgun/router"
|
|
"forge.lightcrystal.systems/nilix/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 {
|
|
Iv []byte
|
|
Secret string
|
|
}
|
|
|
|
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) {
|
|
block, err := aes.NewCipher([]byte(self.Secret))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
plainText := []byte(text)
|
|
cfb := cipher.NewCFBEncrypter(block, self.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, self.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, crypto SymmetricCrypto) http.Handler {
|
|
rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("templates/error.html"))}
|
|
rl := rateLimiter.IndiscriminateRateLimiter{
|
|
Seconds: 5,
|
|
AttemptsAllowed: 5,
|
|
}
|
|
|
|
rtr.Get(`/(?P<cipher>\S+)`, Throttle(WithCrypto(renderer.Template("templates/register.html"), crypto), rl.RateLimit))
|
|
rtr.Post(`/(?P<cipher>\S+)`, Throttle(WithUserStoreAndCrypto(renderer.Template("templates/registered.html"), udb, crypto), rl.RateLimit))
|
|
|
|
return http.HandlerFunc(rtr.ServeHTTP)
|
|
}
|