Starting point for authorization/authentication level stuff, and their storage containers

Signed-off-by: Ethan Wellenreiter <ewellenreiter@gmail.com>
This commit is contained in:
Ethan Wellenreiter 2025-04-30 20:56:35 -04:00
parent b266aa1c34
commit 559aa17da4
5 changed files with 129 additions and 0 deletions

View File

@ -0,0 +1,51 @@
package auth_storage
import (
"context"
"git.ewellenr.ca/receipt_indexer/backend/internal/lcrypto"
"github.com/redis/go-redis/v9"
)
// game plan. store the session token/id and a salt. then, hash them to create the csrf token. give this csrf token out to the user. when the user ends a session, it ends the session but also deletes it from the csrf store
// const csrfSaltLength = 32
// const csrfTokenLength = 128
// should be set by the hasher
type redisCSRF struct {
rdb *redis.Client
hasher lcrypto.Hasher
}
func (r *redisCSRF) AddCSRF(ctx context.Context, sessionToken string) (csrftoken string, err error) {
csrf, err := r.hasher.Hash(sessionToken)
if err != nil {
return "", err
}
csrftoken = string(csrf)
if err = r.rdb.Set(ctx, sessionToken, csrftoken, 0).Err(); err != nil {
return "", err
}
return csrftoken, nil
}
func (r *redisCSRF) RemoveCSRF(ctx context.Context, sessionToken string) error {
r.rdb.Del(ctx, sessionToken)
return nil
}
func (r *redisCSRF) ValidCSRF(ctx context.Context, sessionToken string, csrfToken string) (bool, error) {
storedcsrfToken, err := r.rdb.Get(ctx, sessionToken).Result()
if err != nil {
return false, err
}
valid, err := lcrypto.CompareHash([]byte(csrfToken), []byte(storedcsrfToken))
if err != nil {
return false, err
}
return valid, nil
}

View File

@ -0,0 +1,8 @@
package auth_storage
type Role struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Level int `json:"level"`
}

View File

@ -0,0 +1 @@
package auth_storage

View File

@ -0,0 +1,42 @@
package auth_storage
import (
"context"
"time"
)
type AuthStorage struct {
Users interface { // store user id, username, password(hashed+salted), role?
GetByID(ctx context.Context, id int64) (*User, error)
GetByEmail(context.Context, string) (*User, error)
GetByUsername(context.Context, string) (*User, error)
Create(context.Context, *User) error // create a non-exported create function which does take in the tx
CreateAndInvite(ctx context.Context, user *User, token string, exp time.Duration) error
Activate(context.Context, string) error
Delete(ctx context.Context, id int64) error
UpdateUserPass(ctx context.Context, user string, oldPassword string, newPass string) error
CheckPass(ctx context.Context, name string, pass string) (bool, error)
SigninUser(ctx context.Context, name string, pass string) (bool, *User, error)
// ValidCredentials(ctx context.Context, user *User, pass string) (bool, error)
}
Sessions interface { // store just session tokens, and their corresponding user id
AddSession(ctx context.Context, userid int64) (token string, err error)
CheckSession(ctx context.Context, token string) (valid bool, userid int64, err error) // should also extend it by the lifespan if near the end of the time. maybe a 5 min window at the end?
RemoveSession(ctx context.Context, token string) error
SetLifespan(ctx context.Context, token string, lf time.Time) error
}
CSRF interface {
AddCSRF(ctx context.Context, sessionToken string) (csrftoken string, err error)
RemoveCSRF(ctx context.Context, sessionToken string) error
ValidCSRF(ctx context.Context, sessionToken string, csrfToken string) (bool, error)
// CleanupCSRF()
}
Roles interface {
GetByName(context.Context, string) (*Role, error)
}
}

View File

@ -0,0 +1,27 @@
package auth_storage
import (
"errors"
)
var (
ErrUserNotFound = errors.New("user not found")
ErrExistingUser = errors.New("user already exists")
ErrPasswordIncorrect = errors.New("password incorrect")
)
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Password Password `json:"-"`
CreatedAt string `json:"created_at"`
IsActive bool `json:"is_active"`
Role Role `json:"role"`
}
type Password struct {
text *string
hash []byte
encoded *string
}