This repository has been archived by the owner on Sep 6, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 62
/
account.go
132 lines (115 loc) · 2.66 KB
/
account.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package account
import (
"bytes"
"encoding/gob"
"os"
"path/filepath"
)
type User struct {
Name string
Values map[string]interface{}
store AccountStore
challengeProvider ChallengeProvider
}
func (u *User) UpdateChallenge(password string) {
s, ok := u.Values["_salt"]
var salt []byte
if !ok {
salt = u.challengeProvider.RandomSalt()
u.Values["_salt"] = salt
} else {
salt = s.([]byte)
}
key := u.challengeProvider.DeriveKey(password, salt)
challengeMessage := append(salt, []byte(u.Name)...)
u.Values["_challenge"] = u.challengeProvider.Challenge(challengeMessage, key)
u.Save()
}
func (u *User) Check(password string) bool {
s, ok := u.Values["_salt"]
if !ok {
return false
}
salt := s.([]byte)
key := u.challengeProvider.DeriveKey(password, salt)
challengeMessage := append(salt, []byte(u.Name)...)
newChallenge := u.challengeProvider.Challenge(challengeMessage, key)
return bytes.Equal(newChallenge, u.Values["_challenge"].([]byte))
}
func (u *User) Save() error {
return u.store.Save(u)
}
type AccountStore interface {
Get(string) *User
Create(string) *User
Save(*User) error
}
type ChallengeProvider interface {
DeriveKey(string, []byte) []byte
RandomSalt() []byte
Challenge(message []byte, key []byte) []byte
}
type FilesystemStore struct {
path string
challengeProvider ChallengeProvider
}
func (f *FilesystemStore) Get(name string) *User {
userPath := filepath.Join(f.path, name)
var user *User
file, err := os.Open(userPath)
if err == nil {
defer file.Close()
dec := gob.NewDecoder(file)
var newuser User
err = dec.Decode(&newuser)
if err == nil {
user = &newuser
} else {
panic(err)
}
} else {
if !os.IsNotExist(err) {
panic(err)
}
return nil
}
if user == nil {
return nil
}
user.store = f
user.challengeProvider = f.challengeProvider
return user
}
func (f *FilesystemStore) Create(name string) *User {
userPath := filepath.Join(f.path, name)
_, err := os.Stat(userPath)
if !os.IsNotExist(err) {
return nil
}
return &User{
Name: name,
Values: make(map[string]interface{}),
store: f,
challengeProvider: f.challengeProvider,
}
}
func (f *FilesystemStore) Save(user *User) error {
userPath := filepath.Join(f.path, user.Name)
tempPath := userPath + ".tmp"
file, err := os.Create(tempPath)
if err != nil {
return err
}
defer file.Close()
enc := gob.NewEncoder(file)
err = enc.Encode(user)
if err != nil {
return err
}
os.Remove(userPath)
os.Rename(tempPath, userPath)
return nil
}
func NewFilesystemStore(path string, challengeProvider ChallengeProvider) *FilesystemStore {
return &FilesystemStore{path, challengeProvider}
}