diff --git a/auth/auth.go b/auth/auth.go index 0471b6e..19d1582 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -25,15 +25,15 @@ func Register(w http.ResponseWriter, r *http.Request) { if ok { email = strings.TrimSpace(email) password = strings.TrimSpace(password) - if !(validEmail(email) && validPass(password) && !storage.Data.EmailExists(email)) { - slog.Debug("Outcome", + if !(validEmail(email) && validPass(password) && !storage.Data.Taken(email)) { + slog.Debug("outcome", "email", validEmail(email), "pass", validPass(password), - "taken", !storage.Data.EmailExists(email)) + "taken", !storage.Data.Taken(email)) http.Error(w, "invalid auth credentials", http.StatusBadRequest) return } - err := storage.Data.AddUser(email, password) + err := storage.Data.Add(email, password) if err != nil { slog.Error("error adding a new user", "error", err) http.Error(w, "error adding a new user", http.StatusInternalServerError) @@ -57,18 +57,18 @@ func Login(w http.ResponseWriter, r *http.Request) { email = strings.TrimSpace(email) password = strings.TrimSpace(password) user, ok := storage.Data.ByEmail(email) - if !ok || !user.PasswordFits(password) { + if !ok || !user.Fits(password) { w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) http.Error(w, "you did something wrong", http.StatusUnauthorized) return } - s, err := CreateJWT(user) + token, err := Create(user) if err != nil { http.Error(w, "error creating jwt", http.StatusInternalServerError) return } - w.Write([]byte(s)) + w.Write([]byte(token)) return } diff --git a/auth/jwt.go b/auth/jwt.go index c9210af..22b3a3f 100644 --- a/auth/jwt.go +++ b/auth/jwt.go @@ -18,7 +18,7 @@ import ( var key *rsa.PrivateKey -// LoadKey attempts to load a private key from KeyFile. +// LoadKey attempts to load a private RS256 key from file. // If the file does not exist, it generates a new key (and saves it) func MustLoadKey() { // If the key doesn't exist, create it @@ -58,14 +58,15 @@ func MustLoadKey() { } } -// PublicKey returns our public key as PEM block over http -func PublicKey(w http.ResponseWriter, r *http.Request) { +// ServePublicKey returns our public key as PEM block over HTTP +func ServePublicKey(w http.ResponseWriter, r *http.Request) { key_marshalled := x509.MarshalPKCS1PublicKey(&key.PublicKey) block := pem.Block{Bytes: key_marshalled, Type: "RSA PUBLIC KEY"} pem.Encode(w, &block) } -func CreateJWT(user storage.User) (string, error) { +// Create creates a JSON Web Token that expires after a week +func Create(user storage.User) (token string, err error) { t := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ "iss": "pye", @@ -74,17 +75,17 @@ func CreateJWT(user storage.User) (string, error) { "iat": time.Now().Unix(), "exp": time.Now().Add(time.Hour * 24 * 7).Unix(), }) - s, err := t.SignedString(key) + token, err = t.SignedString(key) if err != nil { slog.Error("error creating JWT", "error", err) return "", err } - return s, nil + return } -// VerifyToken receives a JWT and PEM-encoded public key, +// Verify receives a JWT and PEM-encoded public key, // then returns whether the token is valid -func VerifyJWT(token string, publicKey []byte) (*jwt.Token, error) { +func Verify(token string, publicKey []byte) (*jwt.Token, error) { t, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { key, err := jwt.ParseRSAPublicKeyFromPEM(publicKey) if err != nil { @@ -98,8 +99,9 @@ func VerifyJWT(token string, publicKey []byte) (*jwt.Token, error) { return t, err } -func VerifyLocalJWT(token string) (*jwt.Token, error) { +// VerifyLocal calls Verify with public key set to current local one +func VerifyLocal(token string) (*jwt.Token, error) { key_marshalled := x509.MarshalPKCS1PublicKey(&key.PublicKey) block := pem.Block{Bytes: key_marshalled, Type: "RSA PUBLIC KEY"} - return VerifyJWT(token, pem.EncodeToMemory(&block)) + return Verify(token, pem.EncodeToMemory(&block)) } diff --git a/cmd/root.go b/cmd/root.go index 8f76cdf..c8f97ff 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,14 +25,14 @@ var ( ) func initConfig() { - logging.LogInit(*debugMode) - config.MustLoadConfig(cfgFile) + logging.Load(*debugMode) + config.MustLoad(cfgFile) if cfgDb != "" { config.Cfg.SQLiteFile = cfgDb } auth.MustLoadKey() - storage.Data = sqlite.MustLoadSQLite(config.Cfg.SQLiteFile) + storage.Data = sqlite.MustLoad(config.Cfg.SQLiteFile) } func init() { diff --git a/cmd/serve.go b/cmd/serve.go index 5e572d4..7eb409b 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -35,7 +35,7 @@ func serveAuth(cmd *cobra.Command, args []string) { r.Use(middleware.RealIP) r.Use(middleware.Logger, middleware.CleanPath, middleware.StripSlashes) - r.Get("/pem", auth.PublicKey) + r.Get("/pem", auth.ServePublicKey) r.Post("/register", auth.Register) r.Post("/login", auth.Login) diff --git a/cmd/verify.go b/cmd/verify.go index 0071f6e..59e118a 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -41,14 +41,14 @@ func verifyFunc(cmd *cobra.Command, args []string) { var err error if verifyFile == "" { fmt.Println("No PEM file supplied, assuming local") - t, err = auth.VerifyLocalJWT(verifyToken) + t, err = auth.VerifyLocal(verifyToken) } else { key, err_k := os.ReadFile(verifyFile) if err_k != nil { slog.Error("error reading file", "error", err, "file", verifyFile) return } - t, err = auth.VerifyJWT(verifyToken, key) + t, err = auth.Verify(verifyToken, key) } slog.Debug("result", "token", t, "error", err, "ok", err == nil) if err == nil { diff --git a/config/config.go b/config/config.go index 77b1806..b9859fd 100644 --- a/config/config.go +++ b/config/config.go @@ -27,7 +27,7 @@ var ( Cfg Config ) -func LoadConfig(filename string) error { +func Load(filename string) error { data, err := os.ReadFile(filename) if err != nil { return err @@ -42,8 +42,8 @@ func LoadConfig(filename string) error { return nil } -func MustLoadConfig(filename string) { - err := LoadConfig(filename) +func MustLoad(filename string) { + err := Load(filename) if err != nil { slog.Error("error initially loading config", "error", err) os.Exit(1) diff --git a/logging/log.go b/logging/log.go index b7255b7..752c4e9 100644 --- a/logging/log.go +++ b/logging/log.go @@ -10,8 +10,8 @@ import ( "github.com/go-chi/chi/middleware" ) -// LogInit makes slog output to both os.Stdout and a file if needed, and sets slog.LevelDebug if enabled. -func LogInit(debugMode bool) { +// Load makes slog output to both os.Stdout and a file if needed, and sets slog.LevelDebug if enabled. +func Load(debugMode bool) { var w io.Writer if config.Cfg.LogToFile { f, err := os.OpenFile(config.Cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) diff --git a/storage/sqlite/sqlite.go b/storage/sqlite/sqlite.go index 9844c69..8a93756 100644 --- a/storage/sqlite/sqlite.go +++ b/storage/sqlite/sqlite.go @@ -23,8 +23,8 @@ type SQLiteStorage struct { db *sql.DB } -func (s SQLiteStorage) AddUser(email, password string) error { - user, err := storage.NewUser(email, password) +func (s SQLiteStorage) Add(email, password string) error { + user, err := storage.New(email, password) if err != nil { return err } @@ -53,12 +53,12 @@ func (s SQLiteStorage) ByEmail(email string) (storage.User, bool) { return user, err == nil } -func (s SQLiteStorage) EmailExists(email string) bool { +func (s SQLiteStorage) Taken(email string) bool { _, ok := s.ByEmail(email) return ok } -func MustLoadSQLite(dataFile string) SQLiteStorage { +func MustLoad(dataFile string) SQLiteStorage { if _, err := os.Stat(dataFile); errors.Is(err, os.ErrNotExist) { os.Create(dataFile) slog.Debug("created sqlite3 database file", "file", dataFile) diff --git a/storage/storage.go b/storage/storage.go index ac974a0..c49feed 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,13 +1,13 @@ package storage -// Storage is an arbitrary storage interface +// Storage is an interface for arbitrary storage type Storage interface { - AddUser(email, password string) error - ById(uuid string) (User, bool) - ByEmail(uuid string) (User, bool) - EmailExists(email string) bool + Add(email, password string) error // Add inserts a user into data + ById(uuid string) (User, bool) // ById retrieves a user by their UUID + ByEmail(email string) (User, bool) // ByEmail retrieves a user by their email + Taken(email string) bool // Taken checks whether an email is taken } -// Data stores active information for the app -// It should be populated at app startup +// Data stores active information for the app. +// It should be populated on startup var Data Storage diff --git a/storage/user.go b/storage/user.go index 5c34a5d..52dc33f 100644 --- a/storage/user.go +++ b/storage/user.go @@ -13,12 +13,14 @@ type User struct { Hash []byte // bcrypt hash of password } -func (u User) PasswordFits(password string) bool { +// Fits checks whether the password is correct +func (u User) Fits(password string) bool { err := bcrypt.CompareHashAndPassword(u.Hash, []byte(password)) return err == nil } -func NewUser(email, password string) (User, error) { +// New Creates a new User +func New(email, password string) (User, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), 14) if err != nil { slog.Error("error creating a new user", "error", err)