Updating storage and adding SQL DB schema
Unfinished Signed-off-by: Ethan Wellenreiter <ewellenreiter@gmail.com>
This commit is contained in:
parent
be7a54e7f4
commit
f608f29842
73
backend/internal/db/temp.txt
Normal file
73
backend/internal/db/temp.txt
Normal file
@ -0,0 +1,73 @@
|
||||
// Use DBML to define your database structure
|
||||
// Docs: https://dbml.dbdiagram.io/docs
|
||||
|
||||
Table users {
|
||||
id integer [primary key]
|
||||
username varchar unique
|
||||
email varchar unique
|
||||
password varchar
|
||||
created_at timestamp
|
||||
is_active bool
|
||||
role integer
|
||||
personalgroup integer
|
||||
}
|
||||
|
||||
Table groups {
|
||||
id integer [primary key]
|
||||
name varchar
|
||||
owner integer
|
||||
}
|
||||
|
||||
Table groupMembership {
|
||||
membershipid integer [primary key]
|
||||
groupid integer
|
||||
userid integer
|
||||
moderator bool
|
||||
}
|
||||
|
||||
|
||||
Table roles {
|
||||
id integer [primary key]
|
||||
name varchar
|
||||
description varchar
|
||||
level integer
|
||||
}
|
||||
|
||||
Table reciepts {
|
||||
id integer [primary key]
|
||||
groupownerid integer
|
||||
data nvarchar
|
||||
}
|
||||
|
||||
// Table imageOwnership {
|
||||
// ownershipid integer [primary key]
|
||||
// receiptid integer
|
||||
// imageid integer
|
||||
// }
|
||||
|
||||
Table images {
|
||||
id integer [primary key]
|
||||
receiptid integer
|
||||
created_at timestamp
|
||||
path varchar
|
||||
}
|
||||
|
||||
|
||||
|
||||
Ref: "users"."personalgroup" > "groups"."id"
|
||||
|
||||
Ref: "groups"."owner" > "users"."id"
|
||||
|
||||
Ref: "groups"."id" < "groupMembership"."groupid"
|
||||
|
||||
Ref: "groupMembership"."userid" < "users"."id"
|
||||
|
||||
Ref: "roles"."id" < "users"."role"
|
||||
|
||||
// Ref: "reciepts"."id" < "imageOwnership"."receiptid"
|
||||
|
||||
// Ref: "images"."id" < "imageOwnership"."imageid"
|
||||
|
||||
Ref: "reciepts"."id" < "images"."receiptid"
|
||||
|
||||
Ref: "groups"."id" < "reciepts"."groupownerid"
|
||||
@ -1 +0,0 @@
|
||||
package auth_storage
|
||||
@ -1,42 +0,0 @@
|
||||
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 // figure this out
|
||||
Activate(context.Context, string) error // what does this do?
|
||||
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)
|
||||
CheckCSRF(ctx context.Context, sessionToken string, csrfToken string) (bool, error)
|
||||
RemoveCSRF(ctx context.Context, sessionToken string) error
|
||||
// CleanupCSRF()
|
||||
}
|
||||
|
||||
Roles interface {
|
||||
GetByName(context.Context, string) (*Role, error)
|
||||
}
|
||||
}
|
||||
6
backend/internal/storage/cache/cache.go
vendored
6
backend/internal/storage/cache/cache.go
vendored
@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
|
||||
"git.ewellenr.ca/receipt_indexer/backend/internal/storage"
|
||||
auth_storage "git.ewellenr.ca/receipt_indexer/backend/internal/storage/auth"
|
||||
// auth_storage "git.ewellenr.ca/receipt_indexer/backend/internal/storage/auth"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
Users interface {
|
||||
Get(ctx context.Context, id int64) (*auth_storage.User, error)
|
||||
Set(ctx context.Context, user *auth_storage.User) error
|
||||
Get(ctx context.Context, id int64) (*storage.User, error)
|
||||
Set(ctx context.Context, user *storage.User) error
|
||||
Delete(ctx context.Context, userID int64)
|
||||
}
|
||||
Receipts interface {
|
||||
|
||||
9
backend/internal/storage/cache/redis_user.go
vendored
9
backend/internal/storage/cache/redis_user.go
vendored
@ -6,7 +6,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
auth_storage "git.ewellenr.ca/receipt_indexer/backend/internal/storage/auth"
|
||||
// auth_storage "git.ewellenr.ca/receipt_indexer/backend/internal/storage/auth"
|
||||
"git.ewellenr.ca/receipt_indexer/backend/internal/storage"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
@ -16,7 +17,7 @@ type UserStore struct {
|
||||
|
||||
const UserExpTime = time.Minute
|
||||
|
||||
func (s *UserStore) Get(ctx context.Context, userID int64) (*auth_storage.User, error) {
|
||||
func (s *UserStore) Get(ctx context.Context, userID int64) (*storage.User, error) {
|
||||
cacheKey := fmt.Sprintf("user-%d", userID)
|
||||
|
||||
data, err := s.rdb.Get(ctx, cacheKey).Result()
|
||||
@ -26,7 +27,7 @@ func (s *UserStore) Get(ctx context.Context, userID int64) (*auth_storage.User,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var user auth_storage.User
|
||||
var user storage.User
|
||||
if data != "" {
|
||||
err := json.Unmarshal([]byte(data), &user)
|
||||
if err != nil {
|
||||
@ -37,7 +38,7 @@ func (s *UserStore) Get(ctx context.Context, userID int64) (*auth_storage.User,
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *UserStore) Set(ctx context.Context, user *auth_storage.User) error {
|
||||
func (s *UserStore) Set(ctx context.Context, user *storage.User) error {
|
||||
cacheKey := fmt.Sprintf("user-%d", user.ID)
|
||||
|
||||
json, err := json.Marshal(user)
|
||||
|
||||
@ -3,8 +3,8 @@ package storage
|
||||
import "time"
|
||||
|
||||
type Receipt struct {
|
||||
ID int64 `json:"id"`
|
||||
Owner string `json:"username"`
|
||||
ID int64 `json:"id"`
|
||||
// Owner string `json:"username"`
|
||||
OwnerID int64 `json:"user_id"`
|
||||
ImageIDs []int64 `json:"image_ids"`
|
||||
Data ReceiptData `json:"receipt_data"`
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
package auth_storage
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.ewellenr.ca/receipt_indexer/backend/internal/lcrypto"
|
||||
"github.com/redis/go-redis/v9"
|
||||
@ -15,8 +16,9 @@ import (
|
||||
// should be set by the hasher
|
||||
|
||||
type redisCSRF struct {
|
||||
rdb *redis.Client
|
||||
hasher lcrypto.Hasher
|
||||
rdb *redis.Client
|
||||
hasher lcrypto.Hasher
|
||||
expirationTime uint
|
||||
}
|
||||
|
||||
func (r *redisCSRF) AddCSRF(ctx context.Context, sessionToken string) (csrftoken string, err error) {
|
||||
@ -26,7 +28,7 @@ func (r *redisCSRF) AddCSRF(ctx context.Context, sessionToken string) (csrftoken
|
||||
}
|
||||
csrftoken = string(csrf)
|
||||
|
||||
if err = r.rdb.Set(ctx, sessionToken, csrftoken, 0).Err(); err != nil {
|
||||
if err = r.rdb.Set(ctx, sessionToken, csrftoken, time.Duration(r.expirationTime)).Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return csrftoken, nil
|
||||
@ -43,6 +45,10 @@ func (r *redisCSRF) ValidCSRF(ctx context.Context, sessionToken string, csrfToke
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = r.rdb.ExpireXX(ctx, sessionToken, time.Duration(r.expirationTime)).Err(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
valid, err := lcrypto.CompareHash([]byte(csrfToken), []byte(storedcsrfToken))
|
||||
if err != nil {
|
||||
return false, err
|
||||
59
backend/internal/storage/redis-sessions.go
Normal file
59
backend/internal/storage/redis-sessions.go
Normal file
@ -0,0 +1,59 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"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 redisSession struct {
|
||||
rdb *redis.Client
|
||||
sessionTokenLength uint
|
||||
expirationTime uint
|
||||
}
|
||||
|
||||
func (r *redisSession) AddSession(ctx context.Context, userid int64) (token string, err error) {
|
||||
temp, err := lcrypto.GenerateRandomBytes(r.sessionTokenLength)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token = string(temp)
|
||||
err = r.rdb.Set(ctx, token, userid, time.Duration(r.expirationTime)).Err()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (r *redisSession) GetSession(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?
|
||||
userid, err = r.rdb.Get(ctx, token).Int64()
|
||||
if err == redis.Nil {
|
||||
valid = false
|
||||
userid = -1
|
||||
} else if err != nil {
|
||||
return false, -1, err
|
||||
} else {
|
||||
valid = true
|
||||
}
|
||||
|
||||
err = r.rdb.ExpireXX(ctx, token, time.Duration(r.expirationTime)).Err()
|
||||
if err != nil {
|
||||
return false, -1, err
|
||||
}
|
||||
|
||||
return valid, userid, err
|
||||
}
|
||||
|
||||
func (r *redisSession) RemoveSession(ctx context.Context, token string) error {
|
||||
return r.rdb.Del(ctx, token).Err()
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package auth_storage
|
||||
package storage
|
||||
|
||||
type Role struct {
|
||||
ID int64 `json:"id"`
|
||||
51
backend/internal/storage/sql-users.go
Normal file
51
backend/internal/storage/sql-users.go
Normal file
@ -0,0 +1,51 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SQLUsersStore struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) GetByID(ctx context.Context, id int64) (*User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) GetByEmail(context.Context, string) (*User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) GetByUsername(context.Context, string) (*User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) Create(context.Context, *User) error { // create a non-exported create function which does take in the tx
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) CreateAndInvite(ctx context.Context, user *User, token string, exp time.Duration) error { // figure this out
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) Activate(context.Context, string) error { // what does this do?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) Delete(ctx context.Context, id int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) UpdateUserPass(ctx context.Context, user string, oldPassword string, newPass string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) CheckPass(ctx context.Context, name string, pass string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *SQLUsersStore) SigninUser(ctx context.Context, name string, pass string) (bool, *User, error) {
|
||||
return false, nil, nil
|
||||
}
|
||||
@ -3,9 +3,44 @@ package storage
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Storage 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 // figure this out
|
||||
Activate(context.Context, string) error // what does this do?
|
||||
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)
|
||||
GetSession(ctx context.Context, token string) (valid bool, userid int64, err error) // extends it's expiry
|
||||
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)
|
||||
CheckCSRF(ctx context.Context, sessionToken string, csrfToken string) (bool, error)
|
||||
RemoveCSRF(ctx context.Context, sessionToken string) error
|
||||
// CleanupCSRF()
|
||||
}
|
||||
|
||||
Roles interface {
|
||||
GetByName(context.Context, string) (*Role, error)
|
||||
}
|
||||
|
||||
Receipts interface {
|
||||
GetByID(context.Context, int64) (*Receipt, error)
|
||||
}
|
||||
@ -15,11 +50,14 @@ type Storage struct {
|
||||
}
|
||||
Groups interface {
|
||||
GetByID(context.Context, int64)
|
||||
GetUserGroups(context.Context, int64)
|
||||
}
|
||||
}
|
||||
|
||||
func NewSQLStorage(db *sql.DB) Storage {
|
||||
return Storage{}
|
||||
func NewSQLRedisMinIOStorage(db *sql.DB) Storage {
|
||||
return Storage{
|
||||
Users: &SQLUsersStore{db},
|
||||
}
|
||||
}
|
||||
|
||||
func withTx(db *sql.DB, ctx context.Context, fn func(*sql.Tx) error) error {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package auth_storage
|
||||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
Loading…
Reference in New Issue
Block a user