filestore.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package user
  2. import (
  3. "crypto/subtle"
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "sync"
  8. "golang.org/x/crypto/ssh"
  9. )
  10. // FileStore implements Store using a JSON file
  11. type FileStore struct {
  12. filePath string
  13. users map[string]*User
  14. mu sync.RWMutex
  15. }
  16. // Store defines the interface for user storage and retrieval
  17. type Store interface {
  18. // GetUser retrieves a user by username
  19. GetUser(username string) (*User, error)
  20. // ValidatePassword checks if the password is valid for the user
  21. ValidatePassword(username string, password []byte) bool
  22. // ValidatePublicKey checks if the public key is valid for the user
  23. ValidatePublicKey(username string, keyData string) bool
  24. // GetUserPermissions returns the permissions for a user on a path
  25. GetUserPermissions(username string, path string) []string
  26. // SaveUser saves or updates a user
  27. SaveUser(user *User) error
  28. // DeleteUser removes a user
  29. DeleteUser(username string) error
  30. // ListUsers returns all usernames
  31. ListUsers() ([]string, error)
  32. }
  33. // UserNotFoundError is returned when a user is not found
  34. type UserNotFoundError struct {
  35. Username string
  36. }
  37. func (e *UserNotFoundError) Error() string {
  38. return fmt.Sprintf("user not found: %s", e.Username)
  39. }
  40. // NewFileStore creates a new user store from a JSON file
  41. func NewFileStore(filePath string) (*FileStore, error) {
  42. store := &FileStore{
  43. filePath: filePath,
  44. users: make(map[string]*User),
  45. }
  46. // Create the file if it doesn't exist
  47. if _, err := os.Stat(filePath); os.IsNotExist(err) {
  48. // Create an empty users array
  49. if err := os.WriteFile(filePath, []byte("[]"), 0600); err != nil {
  50. return nil, fmt.Errorf("failed to create user store file: %w", err)
  51. }
  52. }
  53. if err := store.loadUsers(); err != nil {
  54. return nil, err
  55. }
  56. return store, nil
  57. }
  58. // loadUsers loads users from the JSON file
  59. func (s *FileStore) loadUsers() error {
  60. s.mu.Lock()
  61. defer s.mu.Unlock()
  62. data, err := os.ReadFile(s.filePath)
  63. if err != nil {
  64. return fmt.Errorf("failed to read user store file: %w", err)
  65. }
  66. var users []*User
  67. if err := json.Unmarshal(data, &users); err != nil {
  68. return fmt.Errorf("failed to parse user store file: %w", err)
  69. }
  70. // Clear existing users and add the loaded ones
  71. s.users = make(map[string]*User)
  72. for _, user := range users {
  73. // Process public keys to ensure they're in the correct format
  74. for i, keyData := range user.PublicKeys {
  75. // Try to parse the key as an authorized key format
  76. pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keyData))
  77. if err == nil {
  78. // If successful, store the marshaled binary format
  79. user.PublicKeys[i] = string(pubKey.Marshal())
  80. }
  81. }
  82. s.users[user.Username] = user
  83. }
  84. return nil
  85. }
  86. // saveUsers saves users to the JSON file
  87. func (s *FileStore) saveUsers() error {
  88. s.mu.RLock()
  89. defer s.mu.RUnlock()
  90. // Convert map to slice for JSON serialization
  91. var users []*User
  92. for _, user := range s.users {
  93. users = append(users, user)
  94. }
  95. data, err := json.MarshalIndent(users, "", " ")
  96. if err != nil {
  97. return fmt.Errorf("failed to serialize users: %w", err)
  98. }
  99. if err := os.WriteFile(s.filePath, data, 0600); err != nil {
  100. return fmt.Errorf("failed to write user store file: %w", err)
  101. }
  102. return nil
  103. }
  104. // GetUser returns a user by username
  105. func (s *FileStore) GetUser(username string) (*User, error) {
  106. s.mu.RLock()
  107. defer s.mu.RUnlock()
  108. user, ok := s.users[username]
  109. if !ok {
  110. return nil, &UserNotFoundError{Username: username}
  111. }
  112. return user, nil
  113. }
  114. // ValidatePassword checks if the password is valid for the user
  115. func (s *FileStore) ValidatePassword(username string, password []byte) bool {
  116. user, err := s.GetUser(username)
  117. if err != nil {
  118. return false
  119. }
  120. // Compare plaintext password using constant time comparison for security
  121. return subtle.ConstantTimeCompare([]byte(user.Password), password) == 1
  122. }
  123. // ValidatePublicKey checks if the public key is valid for the user
  124. func (s *FileStore) ValidatePublicKey(username string, keyData string) bool {
  125. user, err := s.GetUser(username)
  126. if err != nil {
  127. return false
  128. }
  129. for _, key := range user.PublicKeys {
  130. if subtle.ConstantTimeCompare([]byte(key), []byte(keyData)) == 1 {
  131. return true
  132. }
  133. }
  134. return false
  135. }
  136. // GetUserPermissions returns the permissions for a user on a path
  137. func (s *FileStore) GetUserPermissions(username string, path string) []string {
  138. user, err := s.GetUser(username)
  139. if err != nil {
  140. return nil
  141. }
  142. // Check exact path match first
  143. if perms, ok := user.Permissions[path]; ok {
  144. return perms
  145. }
  146. // Check parent directories
  147. var bestMatch string
  148. var bestPerms []string
  149. for p, perms := range user.Permissions {
  150. if len(p) > len(bestMatch) && os.IsPathSeparator(p[len(p)-1]) && path[:len(p)] == p {
  151. bestMatch = p
  152. bestPerms = perms
  153. }
  154. }
  155. return bestPerms
  156. }
  157. // SaveUser saves or updates a user
  158. func (s *FileStore) SaveUser(user *User) error {
  159. s.mu.Lock()
  160. s.users[user.Username] = user
  161. s.mu.Unlock()
  162. return s.saveUsers()
  163. }
  164. // DeleteUser removes a user
  165. func (s *FileStore) DeleteUser(username string) error {
  166. s.mu.Lock()
  167. _, exists := s.users[username]
  168. if !exists {
  169. s.mu.Unlock()
  170. return &UserNotFoundError{Username: username}
  171. }
  172. delete(s.users, username)
  173. s.mu.Unlock()
  174. return s.saveUsers()
  175. }
  176. // ListUsers returns all usernames
  177. func (s *FileStore) ListUsers() ([]string, error) {
  178. s.mu.RLock()
  179. defer s.mu.RUnlock()
  180. usernames := make([]string, 0, len(s.users))
  181. for username := range s.users {
  182. usernames = append(usernames, username)
  183. }
  184. return usernames, nil
  185. }
  186. // CreateUser creates a new user with the given username and password
  187. func (s *FileStore) CreateUser(username, password string) (*User, error) {
  188. s.mu.Lock()
  189. defer s.mu.Unlock()
  190. // Check if user already exists
  191. if _, exists := s.users[username]; exists {
  192. return nil, fmt.Errorf("user already exists: %s", username)
  193. }
  194. // Create new user
  195. user := NewUser(username)
  196. // Store plaintext password
  197. user.Password = password
  198. // Add default permissions
  199. user.Permissions[user.HomeDir] = []string{"all"}
  200. // Save the user
  201. s.users[username] = user
  202. if err := s.saveUsers(); err != nil {
  203. return nil, err
  204. }
  205. return user, nil
  206. }