Refactor everything again
This commit is contained in:
parent
7fdb0bf0f4
commit
96c369e4b1
14 changed files with 72 additions and 53 deletions
10
README.md
10
README.md
|
@ -1,14 +1,11 @@
|
|||
# Auth microservice
|
||||
# Pye Auth
|
||||
|
||||
**Mission**: Science compels us to create a microservice!
|
||||
|
||||
This is the repository for my **JWT auth microservice assignment**
|
||||
This is the repository for my **JWT authentication microservice**
|
||||
with(out) blazingly fast cloud-native web3 memory-safe blockchain reactive AI
|
||||
(insert a dozen more buzzwords of your choosing) technologies.
|
||||
|
||||
This should be done by **October 17th 2024**. Or, at the very least,
|
||||
in a state that proves I am competent Go developer.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
|
@ -33,6 +30,7 @@ Use "pye [command] --help" for more information about a command.
|
|||
|
||||
## Technologies used
|
||||
|
||||
* **Storage** - [SQLite](https://github.com/mattn/go-sqlite3) and a PEM file
|
||||
* **Storage** - [SQLite](https://github.com/mattn/go-sqlite3) and a
|
||||
[PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) file
|
||||
* **HTTP routing** - [Chi](https://go-chi.io), just for logging...
|
||||
* **CLI management** - [Cobra](https://cobra.dev/)
|
|
@ -1,9 +1,10 @@
|
|||
package cmd
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.a71.su/Andrew71/pye/storage"
|
||||
"git.a71.su/Andrew71/pye/internal/models/user"
|
||||
"git.a71.su/Andrew71/pye/internal/storage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -20,7 +21,7 @@ var findUserCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func findUser(cmd *cobra.Command, args []string) {
|
||||
var user storage.User
|
||||
var user user.User
|
||||
var ok bool
|
||||
if args[0] == "email" {
|
||||
user, ok = storage.Data.ByEmail(args[1])
|
|
@ -1,21 +1,21 @@
|
|||
package cmd
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.a71.su/Andrew71/pye/auth"
|
||||
"git.a71.su/Andrew71/pye/config"
|
||||
"git.a71.su/Andrew71/pye/logging"
|
||||
"git.a71.su/Andrew71/pye/storage"
|
||||
"git.a71.su/Andrew71/pye/storage/sqlite"
|
||||
"git.a71.su/Andrew71/pye/internal/auth"
|
||||
"git.a71.su/Andrew71/pye/internal/config"
|
||||
"git.a71.su/Andrew71/pye/internal/logging"
|
||||
"git.a71.su/Andrew71/pye/internal/storage"
|
||||
"git.a71.su/Andrew71/pye/internal/storage/sqlite"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "pye",
|
||||
Short: "Pye is a simple JWT system",
|
||||
Long: `A bare-bones authentication system built by Andrew71 as an assignment`,
|
||||
Long: `A bare-bones authentication system with RS256`,
|
||||
}
|
||||
|
||||
var (
|
|
@ -1,12 +1,12 @@
|
|||
package cmd
|
||||
package app
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.a71.su/Andrew71/pye/auth"
|
||||
"git.a71.su/Andrew71/pye/config"
|
||||
"git.a71.su/Andrew71/pye/internal/auth"
|
||||
"git.a71.su/Andrew71/pye/internal/config"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/spf13/cobra"
|
|
@ -1,11 +1,11 @@
|
|||
package cmd
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"git.a71.su/Andrew71/pye/auth"
|
||||
"git.a71.su/Andrew71/pye/internal/auth"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
|
@ -1,12 +1,13 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
||||
"git.a71.su/Andrew71/pye/storage"
|
||||
"git.a71.su/Andrew71/pye/internal/storage"
|
||||
)
|
||||
|
||||
func validEmail(email string) bool {
|
||||
|
@ -25,7 +26,7 @@ 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.Taken(email)) {
|
||||
if !(validEmail(email) && validPass(password)) {
|
||||
slog.Debug("outcome",
|
||||
"valid_email", validEmail(email),
|
||||
"valid_pass", validPass(password),
|
||||
|
@ -35,12 +36,15 @@ func Register(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
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)
|
||||
if errors.Is(err, storage.ErrExist) {
|
||||
http.Error(w, "invalid auth credentials", http.StatusBadRequest)
|
||||
} else {
|
||||
http.Error(w, "error adding a new user", http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte("User created"))
|
||||
w.Write([]byte("user created"))
|
||||
return
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"git.a71.su/Andrew71/pye/config"
|
||||
"git.a71.su/Andrew71/pye/storage"
|
||||
"git.a71.su/Andrew71/pye/internal/config"
|
||||
"git.a71.su/Andrew71/pye/internal/models/user"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
|
@ -66,7 +66,7 @@ func ServePublicKey(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Create creates a JSON Web Token that expires after a week
|
||||
func Create(user storage.User) (token string, err error) {
|
||||
func Create(user user.User) (token string, err error) {
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodRS256,
|
||||
jwt.MapClaims{
|
||||
"iss": "pye",
|
|
@ -6,7 +6,7 @@ import (
|
|||
"log/slog"
|
||||
"os"
|
||||
|
||||
"git.a71.su/Andrew71/pye/config"
|
||||
"git.a71.su/Andrew71/pye/internal/config"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package storage
|
||||
package user
|
||||
|
||||
import (
|
||||
"log/slog"
|
|
@ -3,11 +3,13 @@ package sqlite
|
|||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"git.a71.su/Andrew71/pye/storage"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"git.a71.su/Andrew71/pye/internal/models/user"
|
||||
"git.a71.su/Andrew71/pye/internal/storage"
|
||||
sqlite "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
const create string = `
|
||||
|
@ -24,30 +26,36 @@ type SQLiteStorage struct {
|
|||
}
|
||||
|
||||
func (s SQLiteStorage) Add(email, password string) error {
|
||||
user, err := storage.New(email, password)
|
||||
user, err := user.New(email, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.db.Exec("insert into users (uuid, email, password) values ($1, $2, $3)",
|
||||
user.Uuid.String(), user.Email, user.Hash)
|
||||
if err != nil {
|
||||
e, ok := err.(sqlite.Error)
|
||||
if ok && errors.Is(e.ExtendedCode, sqlite.ErrConstraintUnique) {
|
||||
// Return a standard error if the user already exists
|
||||
slog.Info("can't add user because email already taken", "user", user)
|
||||
return fmt.Errorf("%w (%s)", storage.ErrExist, user.Email)
|
||||
}
|
||||
slog.Error("error adding user to database", "error", err, "user", user)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s SQLiteStorage) ById(uuid string) (storage.User, bool) {
|
||||
func (s SQLiteStorage) ById(uuid string) (user.User, bool) {
|
||||
row := s.db.QueryRow("select * from users where uuid = $1", uuid)
|
||||
user := storage.User{}
|
||||
user := user.User{}
|
||||
err := row.Scan(&user.Uuid, &user.Email, &user.Hash)
|
||||
|
||||
return user, err == nil
|
||||
}
|
||||
|
||||
func (s SQLiteStorage) ByEmail(email string) (storage.User, bool) {
|
||||
func (s SQLiteStorage) ByEmail(email string) (user.User, bool) {
|
||||
row := s.db.QueryRow("select * from users where email = $1", email)
|
||||
user := storage.User{}
|
||||
user := user.User{}
|
||||
err := row.Scan(&user.Uuid, &user.Email, &user.Hash)
|
||||
|
||||
return user, err == nil
|
21
internal/storage/storage.go
Normal file
21
internal/storage/storage.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.a71.su/Andrew71/pye/internal/models/user"
|
||||
)
|
||||
|
||||
var ErrExist = errors.New("user already exists")
|
||||
|
||||
// Storage is an interface for arbitrary storage
|
||||
type Storage interface {
|
||||
Add(email, password string) error // Add inserts a user into data
|
||||
ById(uuid string) (user.User, bool) // ById retrieves a user by their UUID
|
||||
ByEmail(email string) (user.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 on startup
|
||||
var Data Storage
|
4
main.go
4
main.go
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
|
||||
import "git.a71.su/Andrew71/pye/cmd"
|
||||
import "git.a71.su/Andrew71/pye/internal/app"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
app.Execute()
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package storage
|
||||
|
||||
// Storage is an interface for arbitrary storage
|
||||
type Storage interface {
|
||||
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 on startup
|
||||
var Data Storage
|
Loading…
Reference in a new issue