diff --git a/go.mod b/go.mod index 4c8704e..ec0faa0 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module git.yetaga.in/alazyreader/alpha go 1.18 + +require golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..27a8544 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= diff --git a/main.go b/main.go index 06ab7d0..7905807 100644 --- a/main.go +++ b/main.go @@ -1 +1,5 @@ package main + +func main() { + +} diff --git a/session.go b/session.go new file mode 100644 index 0000000..34073c1 --- /dev/null +++ b/session.go @@ -0,0 +1,89 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "math" + "time" +) + +var ErrInvalidSession = fmt.Errorf("session not found") + +type User struct { + Name string + Roles []string +} + +type Sess struct { + User User + expr time.Time +} + +type Sessions struct { + sessions map[string]Sess +} + +func (s *Sessions) Create(user User, expr time.Duration) (string, error) { + if s.sessions == nil { + s.sessions = map[string]Sess{} + } + key := randomStr(24) + s.sessions[key] = Sess{ + User: user, + expr: time.Now().Add(expr), + } + return key, nil +} + +func (s *Sessions) Get(key string) (User, error) { + if s.sessions == nil { + s.sessions = map[string]Sess{} + return User{}, ErrInvalidSession + } + sess, ok := s.sessions[key] + if !ok { + return User{}, ErrInvalidSession + } + if sess.expr.After(time.Now()) { + delete(s.sessions, key) + return User{}, ErrInvalidSession + } + return sess.User, nil +} + +func (s *Sessions) Refresh(key string, user User, expr time.Duration) error { + if s.sessions == nil { + s.sessions = map[string]Sess{} + return ErrInvalidSession + } + sess, ok := s.sessions[key] + if !ok { + return ErrInvalidSession + } + if sess.expr.After(time.Now()) { + delete(s.sessions, key) + return ErrInvalidSession + } + s.sessions[key] = Sess{ + User: user, + expr: time.Now().Add(expr), + } + return nil +} + +func (s *Sessions) Destroy(key string) { + if s.sessions == nil { + s.sessions = map[string]Sess{} + return + } + delete(s.sessions, key) +} + +// taken from https://stackoverflow.com/a/55860599 +func randomStr(l int) string { + buff := make([]byte, int(math.Ceil(float64(l)/float64(1.33333333333)))) + rand.Read(buff) + str := base64.RawURLEncoding.EncodeToString(buff) + return str[:l] // strip 1 extra character we get from odd length results +} diff --git a/users.go b/users.go new file mode 100644 index 0000000..bb67bbc --- /dev/null +++ b/users.go @@ -0,0 +1,100 @@ +package main + +import ( + "fmt" + + "golang.org/x/crypto/bcrypt" +) + +var ErrUserNotFound = fmt.Errorf("user not found") + +type storeUser struct { + name string + roles []string + passwordHash []byte +} + +type Userstore struct { + users map[string]storeUser +} + +func (u *Userstore) Create(name string, roles []string, password string) error { + if u.users == nil { + u.users = map[string]storeUser{} + } + hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + u.users[name] = storeUser{ + name: name, + roles: roles, + passwordHash: hashed, + } + return nil +} + +func (u *Userstore) UpdatePassword(name string, password string) error { + if u.users == nil { + u.users = map[string]storeUser{} + return ErrUserNotFound + } + hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + user, ok := u.users[name] + if !ok { + return ErrUserNotFound + } + user.passwordHash = hashed + u.users[name] = user + return nil +} + +func (u *Userstore) UpdateUser(name string, updatedUser User) error { + if u.users == nil { + u.users = map[string]storeUser{} + return ErrUserNotFound + } + user, ok := u.users[name] + if !ok { + return ErrUserNotFound + } + user.name = updatedUser.Name + user.roles = updatedUser.Roles + u.users[name] = user + return nil +} + +func (u *Userstore) Authenticate(name string, password string) (User, error) { + if u.users == nil { + u.users = map[string]storeUser{} + return User{}, ErrUserNotFound + } + user, ok := u.users[name] + if !ok { + return User{}, ErrUserNotFound + } + err := bcrypt.CompareHashAndPassword(user.passwordHash, []byte(password)) + if err != nil { + return User{}, err + } + return User{ + Name: user.name, + Roles: user.roles, + }, nil +} + +func (u *Userstore) Delete(name string) error { + if u.users == nil { + u.users = map[string]storeUser{} + return ErrUserNotFound + } + _, ok := u.users[name] + if !ok { + return ErrUserNotFound + } + delete(u.users, name) + return nil +}