mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-22 07:30:25 +00:00
8b0a1817b3
its main use case is to allow to easily support things like password+OTP for protocols without keyboard interactive support such as FTP and WebDAV
675 lines
18 KiB
Go
675 lines
18 KiB
Go
package dataprovider
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/drakkan/sftpgo/logger"
|
|
"github.com/drakkan/sftpgo/utils"
|
|
"github.com/drakkan/sftpgo/vfs"
|
|
)
|
|
|
|
var (
|
|
errMemoryProviderClosed = errors.New("memory provider is closed")
|
|
)
|
|
|
|
type memoryProviderHandle struct {
|
|
// configuration file to use for loading users
|
|
configFile string
|
|
sync.Mutex
|
|
isClosed bool
|
|
// slice with ordered usernames
|
|
usernames []string
|
|
// mapping between ID and username
|
|
usersIdx map[int64]string
|
|
// map for users, username is the key
|
|
users map[string]User
|
|
// map for virtual folders, MappedPath is the key
|
|
vfolders map[string]vfs.BaseVirtualFolder
|
|
// slice with ordered folders mapped path
|
|
vfoldersPaths []string
|
|
}
|
|
|
|
// MemoryProvider auth provider for a memory store
|
|
type MemoryProvider struct {
|
|
dbHandle *memoryProviderHandle
|
|
}
|
|
|
|
func initializeMemoryProvider(basePath string) error {
|
|
logSender = fmt.Sprintf("dataprovider_%v", MemoryDataProviderName)
|
|
configFile := ""
|
|
if utils.IsFileInputValid(config.Name) {
|
|
configFile = config.Name
|
|
if !filepath.IsAbs(configFile) {
|
|
configFile = filepath.Join(basePath, configFile)
|
|
}
|
|
}
|
|
provider = MemoryProvider{
|
|
dbHandle: &memoryProviderHandle{
|
|
isClosed: false,
|
|
usernames: []string{},
|
|
usersIdx: make(map[int64]string),
|
|
users: make(map[string]User),
|
|
vfolders: make(map[string]vfs.BaseVirtualFolder),
|
|
vfoldersPaths: []string{},
|
|
configFile: configFile,
|
|
},
|
|
}
|
|
return provider.reloadConfig()
|
|
}
|
|
|
|
func (p MemoryProvider) checkAvailability() error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) close() error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
p.dbHandle.isClosed = true
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) validateUserAndPass(username, password, ip, protocol string) (User, error) {
|
|
var user User
|
|
if len(password) == 0 {
|
|
return user, errors.New("Credentials cannot be null or empty")
|
|
}
|
|
user, err := p.userExists(username)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error authenticating user %#v, error: %v", username, err)
|
|
return user, err
|
|
}
|
|
return checkUserAndPass(user, password, ip, protocol)
|
|
}
|
|
|
|
func (p MemoryProvider) validateUserAndPubKey(username string, pubKey []byte) (User, string, error) {
|
|
var user User
|
|
if len(pubKey) == 0 {
|
|
return user, "", errors.New("Credentials cannot be null or empty")
|
|
}
|
|
user, err := p.userExists(username)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error authenticating user %#v, error: %v", username, err)
|
|
return user, "", err
|
|
}
|
|
return checkUserAndPubKey(user, pubKey)
|
|
}
|
|
|
|
func (p MemoryProvider) getUserByID(ID int64) (User, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return User{}, errMemoryProviderClosed
|
|
}
|
|
if val, ok := p.dbHandle.usersIdx[ID]; ok {
|
|
return p.userExistsInternal(val)
|
|
}
|
|
return User{}, &RecordNotFoundError{err: fmt.Sprintf("user with ID %v does not exist", ID)}
|
|
}
|
|
|
|
func (p MemoryProvider) updateLastLogin(username string) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
user, err := p.userExistsInternal(username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
user.LastLogin = utils.GetTimeAsMsSinceEpoch(time.Now())
|
|
p.dbHandle.users[user.Username] = user
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) updateQuota(username string, filesAdd int, sizeAdd int64, reset bool) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
user, err := p.userExistsInternal(username)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "unable to update quota for user %#v error: %v", username, err)
|
|
return err
|
|
}
|
|
if reset {
|
|
user.UsedQuotaSize = sizeAdd
|
|
user.UsedQuotaFiles = filesAdd
|
|
} else {
|
|
user.UsedQuotaSize += sizeAdd
|
|
user.UsedQuotaFiles += filesAdd
|
|
}
|
|
user.LastQuotaUpdate = utils.GetTimeAsMsSinceEpoch(time.Now())
|
|
providerLog(logger.LevelDebug, "quota updated for user %#v, files increment: %v size increment: %v is reset? %v",
|
|
username, filesAdd, sizeAdd, reset)
|
|
p.dbHandle.users[user.Username] = user
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) getUsedQuota(username string) (int, int64, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return 0, 0, errMemoryProviderClosed
|
|
}
|
|
user, err := p.userExistsInternal(username)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "unable to get quota for user %#v error: %v", username, err)
|
|
return 0, 0, err
|
|
}
|
|
return user.UsedQuotaFiles, user.UsedQuotaSize, err
|
|
}
|
|
|
|
func (p MemoryProvider) addUser(user User) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
err := validateUser(&user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = p.userExistsInternal(user.Username)
|
|
if err == nil {
|
|
return fmt.Errorf("username %#v already exists", user.Username)
|
|
}
|
|
user.ID = p.getNextID()
|
|
user.LastQuotaUpdate = 0
|
|
user.UsedQuotaSize = 0
|
|
user.UsedQuotaFiles = 0
|
|
user.LastLogin = 0
|
|
user.VirtualFolders = p.joinVirtualFoldersFields(user)
|
|
p.dbHandle.users[user.Username] = user
|
|
p.dbHandle.usersIdx[user.ID] = user.Username
|
|
p.dbHandle.usernames = append(p.dbHandle.usernames, user.Username)
|
|
sort.Strings(p.dbHandle.usernames)
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) updateUser(user User) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
err := validateUser(&user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u, err := p.userExistsInternal(user.Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, oldFolder := range u.VirtualFolders {
|
|
p.removeUserFromFolderMapping(oldFolder.MappedPath, u.Username)
|
|
}
|
|
user.VirtualFolders = p.joinVirtualFoldersFields(user)
|
|
user.LastQuotaUpdate = u.LastQuotaUpdate
|
|
user.UsedQuotaSize = u.UsedQuotaSize
|
|
user.UsedQuotaFiles = u.UsedQuotaFiles
|
|
user.LastLogin = u.LastLogin
|
|
p.dbHandle.users[user.Username] = user
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) deleteUser(user User) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
u, err := p.userExistsInternal(user.Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, oldFolder := range u.VirtualFolders {
|
|
p.removeUserFromFolderMapping(oldFolder.MappedPath, u.Username)
|
|
}
|
|
delete(p.dbHandle.users, user.Username)
|
|
delete(p.dbHandle.usersIdx, user.ID)
|
|
// this could be more efficient
|
|
p.dbHandle.usernames = []string{}
|
|
for username := range p.dbHandle.users {
|
|
p.dbHandle.usernames = append(p.dbHandle.usernames, username)
|
|
}
|
|
sort.Strings(p.dbHandle.usernames)
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) dumpUsers() ([]User, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
users := make([]User, 0, len(p.dbHandle.usernames))
|
|
var err error
|
|
if p.dbHandle.isClosed {
|
|
return users, errMemoryProviderClosed
|
|
}
|
|
for _, username := range p.dbHandle.usernames {
|
|
user := p.dbHandle.users[username]
|
|
err = addCredentialsToUser(&user)
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
users = append(users, user)
|
|
}
|
|
return users, err
|
|
}
|
|
|
|
func (p MemoryProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
folders := make([]vfs.BaseVirtualFolder, 0, len(p.dbHandle.vfoldersPaths))
|
|
if p.dbHandle.isClosed {
|
|
return folders, errMemoryProviderClosed
|
|
}
|
|
for _, f := range p.dbHandle.vfolders {
|
|
folders = append(folders, f)
|
|
}
|
|
return folders, nil
|
|
}
|
|
|
|
func (p MemoryProvider) getUsers(limit int, offset int, order string, username string) ([]User, error) {
|
|
users := make([]User, 0, limit)
|
|
var err error
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return users, errMemoryProviderClosed
|
|
}
|
|
if limit <= 0 {
|
|
return users, err
|
|
}
|
|
if len(username) > 0 {
|
|
if offset == 0 {
|
|
user, err := p.userExistsInternal(username)
|
|
if err == nil {
|
|
users = append(users, HideUserSensitiveData(&user))
|
|
}
|
|
}
|
|
return users, err
|
|
}
|
|
itNum := 0
|
|
if order == OrderASC {
|
|
for _, username := range p.dbHandle.usernames {
|
|
itNum++
|
|
if itNum <= offset {
|
|
continue
|
|
}
|
|
user := p.dbHandle.users[username]
|
|
users = append(users, HideUserSensitiveData(&user))
|
|
if len(users) >= limit {
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
for i := len(p.dbHandle.usernames) - 1; i >= 0; i-- {
|
|
itNum++
|
|
if itNum <= offset {
|
|
continue
|
|
}
|
|
username := p.dbHandle.usernames[i]
|
|
user := p.dbHandle.users[username]
|
|
users = append(users, HideUserSensitiveData(&user))
|
|
if len(users) >= limit {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return users, err
|
|
}
|
|
|
|
func (p MemoryProvider) userExists(username string) (User, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return User{}, errMemoryProviderClosed
|
|
}
|
|
return p.userExistsInternal(username)
|
|
}
|
|
|
|
func (p MemoryProvider) userExistsInternal(username string) (User, error) {
|
|
if val, ok := p.dbHandle.users[username]; ok {
|
|
return val.getACopy(), nil
|
|
}
|
|
return User{}, &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", username)}
|
|
}
|
|
|
|
func (p MemoryProvider) updateFolderQuota(mappedPath string, filesAdd int, sizeAdd int64, reset bool) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
folder, err := p.folderExistsInternal(mappedPath)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "unable to update quota for folder %#v error: %v", mappedPath, err)
|
|
return err
|
|
}
|
|
if reset {
|
|
folder.UsedQuotaSize = sizeAdd
|
|
folder.UsedQuotaFiles = filesAdd
|
|
} else {
|
|
folder.UsedQuotaSize += sizeAdd
|
|
folder.UsedQuotaFiles += filesAdd
|
|
}
|
|
folder.LastQuotaUpdate = utils.GetTimeAsMsSinceEpoch(time.Now())
|
|
p.dbHandle.vfolders[mappedPath] = folder
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) getUsedFolderQuota(mappedPath string) (int, int64, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return 0, 0, errMemoryProviderClosed
|
|
}
|
|
folder, err := p.folderExistsInternal(mappedPath)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "unable to get quota for folder %#v error: %v", mappedPath, err)
|
|
return 0, 0, err
|
|
}
|
|
return folder.UsedQuotaFiles, folder.UsedQuotaSize, err
|
|
}
|
|
|
|
func (p MemoryProvider) joinVirtualFoldersFields(user User) []vfs.VirtualFolder {
|
|
var folders []vfs.VirtualFolder
|
|
for _, folder := range user.VirtualFolders {
|
|
f, err := p.addOrGetFolderInternal(folder.MappedPath, user.Username, folder.UsedQuotaSize, folder.UsedQuotaFiles,
|
|
folder.LastQuotaUpdate)
|
|
if err == nil {
|
|
folder.UsedQuotaFiles = f.UsedQuotaFiles
|
|
folder.UsedQuotaSize = f.UsedQuotaSize
|
|
folder.LastQuotaUpdate = f.LastQuotaUpdate
|
|
folder.ID = f.ID
|
|
folders = append(folders, folder)
|
|
}
|
|
}
|
|
return folders
|
|
}
|
|
|
|
func (p MemoryProvider) removeUserFromFolderMapping(mappedPath, username string) {
|
|
folder, err := p.folderExistsInternal(mappedPath)
|
|
if err == nil {
|
|
var usernames []string
|
|
for _, user := range folder.Users {
|
|
if user != username {
|
|
usernames = append(usernames, user)
|
|
}
|
|
}
|
|
folder.Users = usernames
|
|
p.dbHandle.vfolders[folder.MappedPath] = folder
|
|
}
|
|
}
|
|
|
|
func (p MemoryProvider) updateFoldersMappingInternal(folder vfs.BaseVirtualFolder) {
|
|
p.dbHandle.vfolders[folder.MappedPath] = folder
|
|
if !utils.IsStringInSlice(folder.MappedPath, p.dbHandle.vfoldersPaths) {
|
|
p.dbHandle.vfoldersPaths = append(p.dbHandle.vfoldersPaths, folder.MappedPath)
|
|
sort.Strings(p.dbHandle.vfoldersPaths)
|
|
}
|
|
}
|
|
|
|
func (p MemoryProvider) addOrGetFolderInternal(mappedPath, username string, usedQuotaSize int64, usedQuotaFiles int, lastQuotaUpdate int64) (vfs.BaseVirtualFolder, error) {
|
|
folder, err := p.folderExistsInternal(mappedPath)
|
|
if _, ok := err.(*RecordNotFoundError); ok {
|
|
folder := vfs.BaseVirtualFolder{
|
|
ID: p.getNextFolderID(),
|
|
MappedPath: mappedPath,
|
|
UsedQuotaSize: usedQuotaSize,
|
|
UsedQuotaFiles: usedQuotaFiles,
|
|
LastQuotaUpdate: lastQuotaUpdate,
|
|
Users: []string{username},
|
|
}
|
|
p.updateFoldersMappingInternal(folder)
|
|
return folder, nil
|
|
}
|
|
if err == nil && !utils.IsStringInSlice(username, folder.Users) {
|
|
folder.Users = append(folder.Users, username)
|
|
p.updateFoldersMappingInternal(folder)
|
|
}
|
|
return folder, err
|
|
}
|
|
|
|
func (p MemoryProvider) folderExistsInternal(mappedPath string) (vfs.BaseVirtualFolder, error) {
|
|
if val, ok := p.dbHandle.vfolders[mappedPath]; ok {
|
|
return val, nil
|
|
}
|
|
return vfs.BaseVirtualFolder{}, &RecordNotFoundError{err: fmt.Sprintf("folder %#v does not exist", mappedPath)}
|
|
}
|
|
|
|
func (p MemoryProvider) getFolders(limit, offset int, order, folderPath string) ([]vfs.BaseVirtualFolder, error) {
|
|
folders := make([]vfs.BaseVirtualFolder, 0, limit)
|
|
var err error
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return folders, errMemoryProviderClosed
|
|
}
|
|
if limit <= 0 {
|
|
return folders, err
|
|
}
|
|
if len(folderPath) > 0 {
|
|
if offset == 0 {
|
|
var folder vfs.BaseVirtualFolder
|
|
folder, err = p.folderExistsInternal(folderPath)
|
|
if err == nil {
|
|
folders = append(folders, folder)
|
|
}
|
|
}
|
|
return folders, err
|
|
}
|
|
itNum := 0
|
|
if order == OrderASC {
|
|
for _, mappedPath := range p.dbHandle.vfoldersPaths {
|
|
itNum++
|
|
if itNum <= offset {
|
|
continue
|
|
}
|
|
folder := p.dbHandle.vfolders[mappedPath]
|
|
folders = append(folders, folder)
|
|
if len(folders) >= limit {
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
for i := len(p.dbHandle.vfoldersPaths) - 1; i >= 0; i-- {
|
|
itNum++
|
|
if itNum <= offset {
|
|
continue
|
|
}
|
|
mappedPath := p.dbHandle.vfoldersPaths[i]
|
|
folder := p.dbHandle.vfolders[mappedPath]
|
|
folders = append(folders, folder)
|
|
if len(folders) >= limit {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return folders, err
|
|
}
|
|
|
|
func (p MemoryProvider) getFolderByPath(mappedPath string) (vfs.BaseVirtualFolder, error) {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return vfs.BaseVirtualFolder{}, errMemoryProviderClosed
|
|
}
|
|
return p.folderExistsInternal(mappedPath)
|
|
}
|
|
|
|
func (p MemoryProvider) addFolder(folder vfs.BaseVirtualFolder) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
err := validateFolder(&folder)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = p.folderExistsInternal(folder.MappedPath)
|
|
if err == nil {
|
|
return fmt.Errorf("folder %#v already exists", folder.MappedPath)
|
|
}
|
|
folder.ID = p.getNextFolderID()
|
|
p.dbHandle.vfolders[folder.MappedPath] = folder
|
|
p.dbHandle.vfoldersPaths = append(p.dbHandle.vfoldersPaths, folder.MappedPath)
|
|
sort.Strings(p.dbHandle.vfoldersPaths)
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) deleteFolder(folder vfs.BaseVirtualFolder) error {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
if p.dbHandle.isClosed {
|
|
return errMemoryProviderClosed
|
|
}
|
|
|
|
_, err := p.folderExistsInternal(folder.MappedPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, username := range folder.Users {
|
|
user, err := p.userExistsInternal(username)
|
|
if err == nil {
|
|
var folders []vfs.VirtualFolder
|
|
for _, userFolder := range user.VirtualFolders {
|
|
if folder.MappedPath != userFolder.MappedPath {
|
|
folders = append(folders, userFolder)
|
|
}
|
|
}
|
|
user.VirtualFolders = folders
|
|
p.dbHandle.users[user.Username] = user
|
|
}
|
|
}
|
|
delete(p.dbHandle.vfolders, folder.MappedPath)
|
|
p.dbHandle.vfoldersPaths = []string{}
|
|
for mappedPath := range p.dbHandle.vfolders {
|
|
p.dbHandle.vfoldersPaths = append(p.dbHandle.vfoldersPaths, mappedPath)
|
|
}
|
|
sort.Strings(p.dbHandle.vfoldersPaths)
|
|
return nil
|
|
}
|
|
|
|
func (p MemoryProvider) getNextID() int64 {
|
|
nextID := int64(1)
|
|
for id := range p.dbHandle.usersIdx {
|
|
if id >= nextID {
|
|
nextID = id + 1
|
|
}
|
|
}
|
|
return nextID
|
|
}
|
|
|
|
func (p MemoryProvider) getNextFolderID() int64 {
|
|
nextID := int64(1)
|
|
for _, v := range p.dbHandle.vfolders {
|
|
if v.ID >= nextID {
|
|
nextID = v.ID + 1
|
|
}
|
|
}
|
|
return nextID
|
|
}
|
|
|
|
func (p MemoryProvider) clear() {
|
|
p.dbHandle.Lock()
|
|
defer p.dbHandle.Unlock()
|
|
p.dbHandle.usernames = []string{}
|
|
p.dbHandle.usersIdx = make(map[int64]string)
|
|
p.dbHandle.users = make(map[string]User)
|
|
p.dbHandle.vfoldersPaths = []string{}
|
|
p.dbHandle.vfolders = make(map[string]vfs.BaseVirtualFolder)
|
|
}
|
|
|
|
func (p MemoryProvider) reloadConfig() error {
|
|
if len(p.dbHandle.configFile) == 0 {
|
|
providerLog(logger.LevelDebug, "no users configuration file defined")
|
|
return nil
|
|
}
|
|
providerLog(logger.LevelDebug, "loading users from file: %#v", p.dbHandle.configFile)
|
|
fi, err := os.Stat(p.dbHandle.configFile)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error loading users: %v", err)
|
|
return err
|
|
}
|
|
if fi.Size() == 0 {
|
|
err = errors.New("users configuration file is invalid, its size must be > 0")
|
|
providerLog(logger.LevelWarn, "error loading users: %v", err)
|
|
return err
|
|
}
|
|
if fi.Size() > 10485760 {
|
|
err = errors.New("users configuration file is invalid, its size must be <= 10485760 bytes")
|
|
providerLog(logger.LevelWarn, "error loading users: %v", err)
|
|
return err
|
|
}
|
|
content, err := ioutil.ReadFile(p.dbHandle.configFile)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error loading users: %v", err)
|
|
return err
|
|
}
|
|
var dump BackupData
|
|
err = json.Unmarshal(content, &dump)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error loading users: %v", err)
|
|
return err
|
|
}
|
|
p.clear()
|
|
for _, folder := range dump.Folders {
|
|
_, err := p.getFolderByPath(folder.MappedPath)
|
|
if err == nil {
|
|
logger.Debug(logSender, "", "folder %#v already exists, restore not needed", folder.MappedPath)
|
|
continue
|
|
}
|
|
folder.Users = nil
|
|
err = p.addFolder(folder)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error adding folder %#v: %v", folder.MappedPath, err)
|
|
return err
|
|
}
|
|
}
|
|
for _, user := range dump.Users {
|
|
u, err := p.userExists(user.Username)
|
|
if err == nil {
|
|
user.ID = u.ID
|
|
err = p.updateUser(user)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error updating user %#v: %v", user.Username, err)
|
|
return err
|
|
}
|
|
} else {
|
|
err = p.addUser(user)
|
|
if err != nil {
|
|
providerLog(logger.LevelWarn, "error adding user %#v: %v", user.Username, err)
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
providerLog(logger.LevelDebug, "user and folders loaded from file: %#v", p.dbHandle.configFile)
|
|
return nil
|
|
}
|
|
|
|
// initializeDatabase does nothing, no initilization is needed for memory provider
|
|
func (p MemoryProvider) initializeDatabase() error {
|
|
return errNoInitRequired
|
|
}
|
|
|
|
func (p MemoryProvider) migrateDatabase() error {
|
|
return nil
|
|
}
|