Skip to content

Commit

Permalink
chore(userAccounts): remove user sessions and files when deleted
Browse files Browse the repository at this point in the history
  • Loading branch information
benjohns1 committed Mar 16, 2024
1 parent df6e815 commit 6de7fdc
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 55 deletions.
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type (
Save(context.Context, Session) error
Get(context.Context, Token) (Session, bool, error)
Delete(context.Context, Token) error
DeleteAllUserSessions(context.Context, blinkfile.UserID) (int, error)
}

FileRepo interface {
Expand Down
10 changes: 6 additions & 4 deletions app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,17 @@ func (sr *SpySessionRepo) Save(ctx context.Context, session app.Session) error {
sr.SaveCalls = append(sr.SaveCalls, session)
return sr.repo.Save(ctx, session)
}

func (sr *SpySessionRepo) Delete(ctx context.Context, token app.Token) error {
sr.DeleteCalls = append(sr.DeleteCalls, token)
return sr.repo.Delete(ctx, token)
}

func (sr *SpySessionRepo) Get(ctx context.Context, token app.Token) (app.Session, bool, error) {
sr.GetCalls = append(sr.GetCalls, token)
return sr.repo.Get(ctx, token)
}
func (sr *SpySessionRepo) DeleteAllUserSessions(context.Context, blinkfile.UserID) (int, error) {
return 0, nil
}

type StubSessionRepo struct {
SaveFunc func(context.Context, app.Session) error
Expand All @@ -89,20 +90,21 @@ func (sr *StubSessionRepo) Save(ctx context.Context, s app.Session) error {
}
return nil
}

func (sr *StubSessionRepo) Get(ctx context.Context, t app.Token) (app.Session, bool, error) {
if sr.GetFunc != nil {
return sr.GetFunc(ctx, t)
}
return app.Session{}, false, nil
}

func (sr *StubSessionRepo) Delete(ctx context.Context, t app.Token) error {
if sr.DeleteFunc != nil {
return sr.DeleteFunc(ctx, t)
}
return nil
}
func (sr *StubSessionRepo) DeleteAllUserSessions(context.Context, blinkfile.UserID) (int, error) {
return 0, nil
}

type StubFileRepo struct {
SaveFunc func(context.Context, blinkfile.File) error
Expand Down
120 changes: 105 additions & 15 deletions app/repo/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"sync"
"time"

"github.com/benjohns1/blinkfile"
Expand All @@ -14,11 +16,15 @@ import (

type (
SessionConfig struct {
Log
Dir string
}

Session struct {
dir string
SessionRepo struct {
mu sync.Mutex
dir string
userIDIndex map[blinkfile.UserID][]app.Token
Log
}

sessionData struct {
Expand All @@ -30,61 +36,145 @@ type (
}
)

func NewSession(cfg SessionConfig) (*Session, error) {
func NewSessionRepo(ctx context.Context, cfg SessionConfig) (*SessionRepo, error) {
dir := filepath.Clean(cfg.Dir)
err := mkdirValidate(dir)
return &Session{dir}, err
if err != nil {
return nil, err
}
r := &SessionRepo{
sync.Mutex{},
dir,
make(map[blinkfile.UserID][]app.Token),
cfg.Log,
}
r.mu.Lock()
defer r.mu.Unlock()
err = r.buildIndices(ctx, dir)
if err != nil {
return nil, err
}
return r, nil
}

func (r *SessionRepo) buildIndices(ctx context.Context, dir string) error {
return filepath.WalkDir(dir, func(path string, f fs.DirEntry, err error) error {
if ctxErr := ctx.Err(); ctxErr != nil {
return ctxErr
}
if err != nil {
r.Errorf(ctx, "Loading session from %q: %v", path, err)
return nil
}
if path == dir {
return nil
}
if f.IsDir() {
return nil
}
sess, err := loadSession(path)
if err != nil {
r.Errorf(ctx, "Loading session data %q: %v", path, err)
return nil
}
r.addToIndices(sess)
return nil
})
}

func loadSession(path string) (sess sessionData, err error) {
data, err := ReadFile(path)
if err != nil {
return sess, err
}
return sess, Unmarshal(data, &sess)
}

func (r *Session) Save(_ context.Context, session app.Session) error {
func (r *SessionRepo) addToIndices(sess sessionData) {
r.userIDIndex[sess.UserID] = append(r.userIDIndex[sess.UserID], sess.Token)
}

func (r *SessionRepo) removeFromIndices(ctx context.Context, token app.Token) {
sess, err := loadSession(r.filename(token))
if err != nil {
r.Errorf(ctx, "getting session to remove for token %q: %v", token, err)
} else {
delete(r.userIDIndex, sess.UserID)
}
}

func (r *SessionRepo) Save(_ context.Context, session app.Session) error {
if session.Token == "" {
return fmt.Errorf("token cannot be empty")
}
data, err := Marshal(sessionData(session))
sd := sessionData(session)
data, err := Marshal(sd)
if err != nil {
return err
}
err = WriteFile(r.filename(session.Token), data, 0644)
if err != nil {
return err
}
r.mu.Lock()
defer r.mu.Unlock()
r.addToIndices(sd)
return nil
}

func (r *Session) filename(token app.Token) string {
func (r *SessionRepo) filename(token app.Token) string {
return fmt.Sprintf("%s/%s.json", r.dir, token)
}

func (r *Session) Get(_ context.Context, token app.Token) (app.Session, bool, error) {
func (r *SessionRepo) Get(_ context.Context, token app.Token) (app.Session, bool, error) {
if token == "" {
return app.Session{}, false, fmt.Errorf("token cannot be empty")
}
data, err := ReadFile(r.filename(token))
sd, err := loadSession(r.filename(token))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil
}
return app.Session{}, false, err
}
var sd sessionData
err = Unmarshal(data, &sd)
if err != nil {
return app.Session{}, false, err
}
session := app.Session(sd)
session.Token = token
return session, true, nil
}

func (r *Session) Delete(_ context.Context, token app.Token) error {
func (r *SessionRepo) DeleteAllUserSessions(ctx context.Context, userID blinkfile.UserID) (int, error) {
if userID == "" {
return 0, fmt.Errorf("user ID cannot be empty")
}
r.mu.Lock()
defer r.mu.Unlock()
var count int
for _, token := range r.userIDIndex[userID] {
err := r.delete(ctx, token)
if err != nil {
return count, err
}
count++
}
return count, nil
}

func (r *SessionRepo) Delete(ctx context.Context, token app.Token) error {
if token == "" {
return fmt.Errorf("token cannot be empty")
}
r.mu.Lock()
defer r.mu.Unlock()
return r.delete(ctx, token)
}

func (r *SessionRepo) delete(ctx context.Context, token app.Token) error {
err := RemoveFile(r.filename(token))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return err
}
}
r.removeFromIndices(ctx, token)
return nil
}
Loading

0 comments on commit 6de7fdc

Please sign in to comment.