Browse Source

squash database migrations and remove compat code

Nicola Murino 4 years ago
parent
commit
49830516be
13 changed files with 207 additions and 2012 deletions
  1. 22 7
      README.md
  2. 3 3
      cmd/revertprovider.go
  3. 20 469
      dataprovider/bolt.go
  4. 0 358
      dataprovider/compat.go
  5. 1 75
      dataprovider/dataprovider.go
  6. 38 231
      dataprovider/mysql.go
  7. 43 234
      dataprovider/pgsql.go
  8. 5 295
      dataprovider/sqlcommon.go
  9. 37 294
      dataprovider/sqlite.go
  10. 0 12
      dataprovider/sqlqueries.go
  11. 12 10
      go.mod
  12. 25 23
      go.sum
  13. 1 1
      version/version.go

+ 22 - 7
README.md

@@ -120,27 +120,42 @@ sftpgo initprovider --help
 
 You can disable automatic data provider checks/updates at startup by setting the `update_mode` configuration key to `1`.
 
-If for some reason you want to downgrade SFTPGo, you may need to downgrade your data provider schema and data as well. You can use the `revertprovider` command for this task.
+## Upgrading
+
+SFTPGo supports upgrading from the previous release branch to the current one.
+Some examples for supported upgrade paths are:
+
+- from 1.2.x to 2.0.x
+- from 2.0.x to 2.1.x and so on.
+
+For supported upgrade paths, the data and schema are migrated automatically, alternately you can use the `initprovider` command.
+
+So if, for example, you want to upgrade from a version before 1.2.x to 2.0.x, you must first install version 1.2.x, update the data provider and finally install the version 2.0.x. It is recommended to always install the latest available minor version, ie do not install 1.2.0 if 1.2.2 is available.
+
+Loading data from a provider independent JSON dump is supported from the previous release branch to the current one too. After updating SFTPGo it is advisable to load the old dump and regenerate it from the new version.
 
-We support the follwing schema versions:
+## Downgrading
 
-- `8`, this is the latest version
-- `4`, this is the schema for v1.0.0-v1.2.x
+If for some reason you want to downgrade SFTPGo, you may need to downgrade your data provider schema and data as well. You can use the `revertprovider` command for this task.
+
+As for upgrading, SFTPGo supports downgrading from the previous release branch to the current one.
 
-So, if you plan to downgrade from 2.0.x to 1.2.x, you can prepare your data provider executing the following command from the configuration directory:
+So, if you plan to downgrade from 2.0.x to 1.2.x, before uninstalling 2.0.x version, you can prepare your data provider executing the following command from the configuration directory:
 
 ```shell
 sftpgo revertprovider --to-version 4
 ```
 
-Take a look at the CLI usage to learn how to specify a different configuration file:
+Take a look at the CLI usage to see the supported parameter for the `--to-version` argument and to learn how to specify a different configuration file:
 
-```bash
+```shell
 sftpgo revertprovider --help
 ```
 
 The `revertprovider` command is not supported for the memory provider.
 
+Please note that we only support the current release branch and the current main branch, if you find a bug it is better to report it rather than downgrading to an older unsupported version.
+
 ## Users and folders management
 
 After starting SFTPGo you can manage users and folders using:

+ 3 - 3
cmd/revertprovider.go

@@ -26,8 +26,8 @@ Please take a look at the usage below to customize the options.`,
 		Run: func(cmd *cobra.Command, args []string) {
 			logger.DisableLogger()
 			logger.EnableConsoleLogger(zerolog.DebugLevel)
-			if revertProviderTargetVersion != 4 {
-				logger.WarnToConsole("Unsupported target version, 4 is the only supported one")
+			if revertProviderTargetVersion != 8 {
+				logger.WarnToConsole("Unsupported target version, 8 is the only supported one")
 				os.Exit(1)
 			}
 			configDir = utils.CleanDirInput(configDir)
@@ -57,7 +57,7 @@ Please take a look at the usage below to customize the options.`,
 
 func init() {
 	addConfigFlags(revertProviderCmd)
-	revertProviderCmd.Flags().IntVar(&revertProviderTargetVersion, "to-version", 0, `4 means the version supported in v1.0.0-v1.2.x`)
+	revertProviderCmd.Flags().IntVar(&revertProviderTargetVersion, "to-version", 0, `8 means the version supported in v2.0.x`)
 	revertProviderCmd.MarkFlagRequired("to-version") //nolint:errcheck
 
 	rootCmd.AddCommand(revertProviderCmd)

+ 20 - 469
dataprovider/bolt.go

@@ -836,30 +836,24 @@ func (p *BoltProvider) migrateDatabase() error {
 	if err != nil {
 		return err
 	}
-	if dbVersion.Version == boltDatabaseVersion {
-		providerLog(logger.LevelDebug, "bolt database is up to date, current version: %v", dbVersion.Version)
+	switch version := dbVersion.Version; {
+	case version == boltDatabaseVersion:
+		providerLog(logger.LevelDebug, "bolt database is up to date, current version: %v", version)
 		return ErrNoInitRequired
-	}
-	switch dbVersion.Version {
-	case 1:
-		return updateBoltDatabaseFromV1(p.dbHandle)
-	case 2:
-		return updateBoltDatabaseFromV2(p.dbHandle)
-	case 3:
-		return updateBoltDatabaseFromV3(p.dbHandle)
-	case 4:
-		return updateBoltDatabaseFromV4(p.dbHandle)
-	case 5:
-		return updateBoltDatabaseFromV5(p.dbHandle)
+	case version < 6:
+		err = fmt.Errorf("database version %v is too old, please see the upgrading docs", version)
+		providerLog(logger.LevelError, "%v", err)
+		logger.ErrorToConsole("%v", err)
+		return err
 	default:
-		if dbVersion.Version > boltDatabaseVersion {
-			providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
+		if version > boltDatabaseVersion {
+			providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
 				boltDatabaseVersion)
-			logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
+			logger.WarnToConsole("database version %v is newer than the supported one: %v", version,
 				boltDatabaseVersion)
 			return nil
 		}
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
+		return fmt.Errorf("Database version not handled: %v", version)
 	}
 }
 
@@ -868,57 +862,13 @@ func (p *BoltProvider) revertDatabase(targetVersion int) error {
 	if err != nil {
 		return err
 	}
-	if dbVersion.Version == targetVersion {
-		return fmt.Errorf("current version match target version, nothing to do")
-	}
-	switch dbVersion.Version {
-	case 5:
-		return downgradeBoltDatabaseFrom5To4(p.dbHandle)
-	case 6:
-		err := downgradeBoltDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeBoltDatabaseFrom5To4(p.dbHandle)
-	default:
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
-	}
-}
-
-func updateBoltDatabaseFromV1(dbHandle *bolt.DB) error {
-	err := updateDatabaseFrom1To2(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateBoltDatabaseFromV2(dbHandle)
-}
-
-func updateBoltDatabaseFromV2(dbHandle *bolt.DB) error {
-	err := updateDatabaseFrom2To3(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateBoltDatabaseFromV3(dbHandle)
-}
-
-func updateBoltDatabaseFromV3(dbHandle *bolt.DB) error {
-	err := updateDatabaseFrom3To4(dbHandle)
-	if err != nil {
-		return err
+	if targetVersion == 8 {
+		targetVersion = 6
 	}
-	return updateBoltDatabaseFromV4(dbHandle)
-}
-
-func updateBoltDatabaseFromV4(dbHandle *bolt.DB) error {
-	err := updateDatabaseFrom4To5(dbHandle)
-	if err != nil {
-		return err
+	if dbVersion.Version == targetVersion {
+		return errors.New("current version match target version, nothing to do")
 	}
-	return updateBoltDatabaseFromV5(dbHandle)
-}
-
-func updateBoltDatabaseFromV5(dbHandle *bolt.DB) error {
-	return updateDatabaseFrom5To6(dbHandle)
+	return errors.New("the current version cannot be reverted")
 }
 
 func joinUserAndFolders(u []byte, foldersBucket *bolt.Bucket) (User, error) {
@@ -1029,44 +979,6 @@ func removeUserFromFolderMapping(folder vfs.VirtualFolder, user *User, bucket *b
 	return err
 }
 
-func updateV4BoltCompatUser(dbHandle *bolt.DB, user compatUserV4) error {
-	return dbHandle.Update(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		if u := bucket.Get([]byte(user.Username)); u == nil {
-			return &RecordNotFoundError{err: fmt.Sprintf("username %v does not exist", user.Username)}
-		}
-		buf, err := json.Marshal(user)
-		if err != nil {
-			return err
-		}
-		return bucket.Put([]byte(user.Username), buf)
-	})
-}
-
-func updateV4BoltUser(dbHandle *bolt.DB, user User) error {
-	err := ValidateUser(&user)
-	if err != nil {
-		return err
-	}
-	return dbHandle.Update(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		if u := bucket.Get([]byte(user.Username)); u == nil {
-			return &RecordNotFoundError{err: fmt.Sprintf("username %v does not exist", user.Username)}
-		}
-		buf, err := json.Marshal(user)
-		if err != nil {
-			return err
-		}
-		return bucket.Put([]byte(user.Username), buf)
-	})
-}
-
 func getAdminBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 	var err error
 
@@ -1095,367 +1007,6 @@ func getFolderBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 	return bucket, err
 }
 
-func updateDatabaseFrom1To2(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("updating bolt database version: 1 -> 2")
-	providerLog(logger.LevelInfo, "updating bolt database version: 1 -> 2")
-	usernames, err := getBoltAvailableUsernames(dbHandle)
-	if err != nil {
-		return err
-	}
-	for _, u := range usernames {
-		user, err := provider.userExists(u)
-		if err != nil {
-			return err
-		}
-		user.Status = 1
-		err = provider.updateUser(&user)
-		if err != nil {
-			return err
-		}
-		providerLog(logger.LevelInfo, "user %#v updated, \"status\" setted to 1", user.Username)
-	}
-	return updateBoltDatabaseVersion(dbHandle, 2)
-}
-
-func updateDatabaseFrom2To3(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("updating bolt database version: 2 -> 3")
-	providerLog(logger.LevelInfo, "updating bolt database version: 2 -> 3")
-	users := []User{}
-	err := dbHandle.View(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
-			var compatUser compatUserV2
-			err = json.Unmarshal(v, &compatUser)
-			if err == nil {
-				user := User{}
-				user.ID = compatUser.ID
-				user.Username = compatUser.Username
-				user.Password = compatUser.Password
-				user.PublicKeys = compatUser.PublicKeys
-				user.HomeDir = compatUser.HomeDir
-				user.UID = compatUser.UID
-				user.GID = compatUser.GID
-				user.MaxSessions = compatUser.MaxSessions
-				user.QuotaSize = compatUser.QuotaSize
-				user.QuotaFiles = compatUser.QuotaFiles
-				user.Permissions = make(map[string][]string)
-				user.Permissions["/"] = compatUser.Permissions
-				user.UsedQuotaSize = compatUser.UsedQuotaSize
-				user.UsedQuotaFiles = compatUser.UsedQuotaFiles
-				user.LastQuotaUpdate = compatUser.LastQuotaUpdate
-				user.UploadBandwidth = compatUser.UploadBandwidth
-				user.DownloadBandwidth = compatUser.DownloadBandwidth
-				user.ExpirationDate = compatUser.ExpirationDate
-				user.LastLogin = compatUser.LastLogin
-				user.Status = compatUser.Status
-				users = append(users, user)
-			}
-		}
-		return err
-	})
-	if err != nil {
-		return err
-	}
-
-	for _, user := range users {
-		user := user
-		err = provider.updateUser(&user)
-		if err != nil {
-			return err
-		}
-		providerLog(logger.LevelInfo, "user %#v updated, \"permissions\" setted to %+v", user.Username, user.Permissions)
-	}
-
-	return updateBoltDatabaseVersion(dbHandle, 3)
-}
-
-func updateDatabaseFrom3To4(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("updating bolt database version: 3 -> 4")
-	providerLog(logger.LevelInfo, "updating bolt database version: 3 -> 4")
-	foldersToScan := []string{}
-	users := []userCompactVFolders{}
-	err := dbHandle.View(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
-			var compatUser userCompactVFolders
-			err = json.Unmarshal(v, &compatUser)
-			if err == nil && len(compatUser.VirtualFolders) > 0 {
-				users = append(users, compatUser)
-			}
-		}
-		return err
-	})
-	if err != nil {
-		return err
-	}
-	for _, u := range users {
-		user, err := provider.userExists(u.Username)
-		if err != nil {
-			return err
-		}
-		var folders []vfs.VirtualFolder
-		for _, f := range u.VirtualFolders {
-			providerLog(logger.LevelInfo, "restoring virtual folder: %+v for user %#v", f, user.Username)
-			quotaSize := int64(-1)
-			quotaFiles := -1
-			if f.ExcludeFromQuota {
-				quotaSize = 0
-				quotaFiles = 0
-			}
-			folder := vfs.VirtualFolder{
-				QuotaSize:   quotaSize,
-				QuotaFiles:  quotaFiles,
-				VirtualPath: f.VirtualPath,
-			}
-			folder.MappedPath = f.MappedPath
-			folders = append(folders, folder)
-			if !utils.IsStringInSlice(folder.MappedPath, foldersToScan) {
-				foldersToScan = append(foldersToScan, folder.MappedPath)
-			}
-		}
-		user.VirtualFolders = folders
-		err = provider.updateUser(&user)
-		providerLog(logger.LevelInfo, "number of virtual folders to restore %v, user %#v, error: %v", len(user.VirtualFolders),
-			user.Username, err)
-		if err != nil {
-			return err
-		}
-	}
-
-	return updateBoltDatabaseVersion(dbHandle, 4)
-	/*if err == nil {
-		go updateVFoldersQuotaAfterRestore(foldersToScan)
-	}
-	return err*/
-}
-
-//nolint:dupl
-func downgradeBoltDatabaseFrom5To4(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("downgrading bolt database version: 5 -> 4")
-	providerLog(logger.LevelInfo, "downgrading bolt database version: 5 -> 4")
-	users := []compatUserV4{}
-	err := dbHandle.View(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
-			var user User
-			err = json.Unmarshal(v, &user)
-			if err != nil {
-				logger.WarnToConsole("failed to unmarshal user %#v to v4, is it already migrated?", string(k))
-				continue
-			}
-			fsConfig, err := convertFsConfigToV4(user.FsConfig, user.Username)
-			if err != nil {
-				return err
-			}
-			users = append(users, convertUserToV4(user, fsConfig))
-		}
-		return nil
-	})
-	if err != nil {
-		return err
-	}
-
-	for _, user := range users {
-		err = updateV4BoltCompatUser(dbHandle, user)
-		if err != nil {
-			return err
-		}
-		providerLog(logger.LevelInfo, "filesystem config updated for user %#v", user.Username)
-	}
-
-	return updateBoltDatabaseVersion(dbHandle, 4)
-}
-
-//nolint:dupl
-func updateDatabaseFrom4To5(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("updating bolt database version: 4 -> 5")
-	providerLog(logger.LevelInfo, "updating bolt database version: 4 -> 5")
-	users := []User{}
-	err := dbHandle.View(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
-			var compatUser compatUserV4
-			err = json.Unmarshal(v, &compatUser)
-			if err != nil {
-				logger.WarnToConsole("failed to unmarshal v4 user %#v, is it already migrated?", string(k))
-				continue
-			}
-			fsConfig, err := convertFsConfigFromV4(compatUser.FsConfig, compatUser.Username)
-			if err != nil {
-				return err
-			}
-			users = append(users, createUserFromV4(compatUser, fsConfig))
-		}
-		return nil
-	})
-	if err != nil {
-		return err
-	}
-
-	for _, user := range users {
-		err = updateV4BoltUser(dbHandle, user)
-		if err != nil {
-			return err
-		}
-		providerLog(logger.LevelInfo, "filesystem config updated for user %#v", user.Username)
-	}
-
-	return updateBoltDatabaseVersion(dbHandle, 5)
-}
-
-// this compat code will be removed after 2.0.0, ignore the lint warning for now
-//nolint:gocyclo
-func updateDatabaseFrom5To6(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("updating bolt database version: 5 -> 6")
-	providerLog(logger.LevelInfo, "updating bolt database version: 5 -> 6")
-	err := dbHandle.Update(func(tx *bolt.Tx) error {
-		bucket, err := getFolderBucket(tx)
-		if err != nil {
-			return err
-		}
-		usersBucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
-			if filepath.IsAbs(string(k)) {
-				var folder vfs.BaseVirtualFolder
-				err = json.Unmarshal(v, &folder)
-				if err != nil {
-					return err
-				}
-				folder.Name = fmt.Sprintf("Folder%v", folder.ID)
-				buf, err := json.Marshal(folder)
-				if err != nil {
-					return err
-				}
-				// insert the folder with the new structure
-				err = bucket.Put([]byte(folder.Name), buf)
-				if err != nil {
-					return err
-				}
-				// delete the folder with the old structure
-				err = bucket.Delete(k)
-				if err != nil {
-					return err
-				}
-				// update users mapping
-				for _, username := range folder.Users {
-					var u []byte
-					if u = usersBucket.Get([]byte(username)); u == nil {
-						continue
-					}
-					var user User
-					err = json.Unmarshal(u, &user)
-					if err != nil {
-						return err
-					}
-					var folders []vfs.VirtualFolder
-					for _, userFolder := range user.VirtualFolders {
-						if folder.MappedPath == userFolder.MappedPath {
-							userFolder.Name = folder.Name
-						}
-						folders = append(folders, userFolder)
-					}
-					user.VirtualFolders = folders
-					buf, err := json.Marshal(user)
-					if err != nil {
-						return err
-					}
-					err = usersBucket.Put([]byte(user.Username), buf)
-					if err != nil {
-						return err
-					}
-				}
-			}
-		}
-		return nil
-	})
-	if err != nil {
-		return err
-	}
-	return updateBoltDatabaseVersion(dbHandle, 6)
-}
-
-func downgradeBoltDatabaseFrom6To5(dbHandle *bolt.DB) error {
-	logger.InfoToConsole("downgrading bolt database version: 6 -> 5")
-	providerLog(logger.LevelInfo, "downgrading bolt database version: 6 -> 5")
-	// best effort we'll remove this code soon
-	err := dbHandle.Update(func(tx *bolt.Tx) error {
-		// just update the folder keys
-		bucket, err := getFolderBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
-			if !filepath.IsAbs(string(k)) {
-				var folder vfs.BaseVirtualFolder
-				err = json.Unmarshal(v, &folder)
-				if err != nil {
-					return err
-				}
-				if filepath.IsAbs(folder.MappedPath) {
-					buf, err := json.Marshal(folder)
-					if err != nil {
-						return err
-					}
-					// insert the folder with the old key
-					err = bucket.Put([]byte(folder.MappedPath), buf)
-					if err != nil {
-						return err
-					}
-					// delete the folder with the new key
-					err = bucket.Delete(k)
-					if err != nil {
-						return err
-					}
-				}
-			}
-		}
-
-		return nil
-	})
-	if err != nil {
-		return err
-	}
-	return updateBoltDatabaseVersion(dbHandle, 5)
-}
-
-func getBoltAvailableUsernames(dbHandle *bolt.DB) ([]string, error) {
-	usernames := []string{}
-	err := dbHandle.View(func(tx *bolt.Tx) error {
-		bucket, err := getUsersBucket(tx)
-		if err != nil {
-			return err
-		}
-		cursor := bucket.Cursor()
-		for k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {
-			usernames = append(usernames, string(k))
-		}
-		return nil
-	})
-
-	return usernames, err
-}
-
 func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
 	var dbVersion schemaVersion
 	err := dbHandle.View(func(tx *bolt.Tx) error {
@@ -1466,7 +1017,7 @@ func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
 		v := bucket.Get(dbVersionKey)
 		if v == nil {
 			dbVersion = schemaVersion{
-				Version: 1,
+				Version: 6,
 			}
 			return nil
 		}
@@ -1475,7 +1026,7 @@ func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
 	return dbVersion, err
 }
 
-func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
+/*func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
 	err := dbHandle.Update(func(tx *bolt.Tx) error {
 		bucket := tx.Bucket(dbVersionBucket)
 		if bucket == nil {
@@ -1491,4 +1042,4 @@ func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
 		return bucket.Put(dbVersionKey, buf)
 	})
 	return err
-}
+}*/

+ 0 - 358
dataprovider/compat.go

@@ -1,358 +0,0 @@
-package dataprovider
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"path/filepath"
-
-	"github.com/drakkan/sftpgo/kms"
-	"github.com/drakkan/sftpgo/logger"
-	"github.com/drakkan/sftpgo/utils"
-	"github.com/drakkan/sftpgo/vfs"
-)
-
-type compatUserV2 struct {
-	ID                int64    `json:"id"`
-	Username          string   `json:"username"`
-	Password          string   `json:"password,omitempty"`
-	PublicKeys        []string `json:"public_keys,omitempty"`
-	HomeDir           string   `json:"home_dir"`
-	UID               int      `json:"uid"`
-	GID               int      `json:"gid"`
-	MaxSessions       int      `json:"max_sessions"`
-	QuotaSize         int64    `json:"quota_size"`
-	QuotaFiles        int      `json:"quota_files"`
-	Permissions       []string `json:"permissions"`
-	UsedQuotaSize     int64    `json:"used_quota_size"`
-	UsedQuotaFiles    int      `json:"used_quota_files"`
-	LastQuotaUpdate   int64    `json:"last_quota_update"`
-	UploadBandwidth   int64    `json:"upload_bandwidth"`
-	DownloadBandwidth int64    `json:"download_bandwidth"`
-	ExpirationDate    int64    `json:"expiration_date"`
-	LastLogin         int64    `json:"last_login"`
-	Status            int      `json:"status"`
-}
-
-type compatS3FsConfigV4 struct {
-	Bucket            string `json:"bucket,omitempty"`
-	KeyPrefix         string `json:"key_prefix,omitempty"`
-	Region            string `json:"region,omitempty"`
-	AccessKey         string `json:"access_key,omitempty"`
-	AccessSecret      string `json:"access_secret,omitempty"`
-	Endpoint          string `json:"endpoint,omitempty"`
-	StorageClass      string `json:"storage_class,omitempty"`
-	UploadPartSize    int64  `json:"upload_part_size,omitempty"`
-	UploadConcurrency int    `json:"upload_concurrency,omitempty"`
-}
-
-type compatGCSFsConfigV4 struct {
-	Bucket               string `json:"bucket,omitempty"`
-	KeyPrefix            string `json:"key_prefix,omitempty"`
-	CredentialFile       string `json:"-"`
-	Credentials          []byte `json:"credentials,omitempty"`
-	AutomaticCredentials int    `json:"automatic_credentials,omitempty"`
-	StorageClass         string `json:"storage_class,omitempty"`
-}
-
-type compatAzBlobFsConfigV4 struct {
-	Container         string `json:"container,omitempty"`
-	AccountName       string `json:"account_name,omitempty"`
-	AccountKey        string `json:"account_key,omitempty"`
-	Endpoint          string `json:"endpoint,omitempty"`
-	SASURL            string `json:"sas_url,omitempty"`
-	KeyPrefix         string `json:"key_prefix,omitempty"`
-	UploadPartSize    int64  `json:"upload_part_size,omitempty"`
-	UploadConcurrency int    `json:"upload_concurrency,omitempty"`
-	UseEmulator       bool   `json:"use_emulator,omitempty"`
-	AccessTier        string `json:"access_tier,omitempty"`
-}
-
-type compatFilesystemV4 struct {
-	Provider     FilesystemProvider     `json:"provider"`
-	S3Config     compatS3FsConfigV4     `json:"s3config,omitempty"`
-	GCSConfig    compatGCSFsConfigV4    `json:"gcsconfig,omitempty"`
-	AzBlobConfig compatAzBlobFsConfigV4 `json:"azblobconfig,omitempty"`
-}
-
-type compatUserV4 struct {
-	ID                int64               `json:"id"`
-	Status            int                 `json:"status"`
-	Username          string              `json:"username"`
-	ExpirationDate    int64               `json:"expiration_date"`
-	Password          string              `json:"password,omitempty"`
-	PublicKeys        []string            `json:"public_keys,omitempty"`
-	HomeDir           string              `json:"home_dir"`
-	VirtualFolders    []vfs.VirtualFolder `json:"virtual_folders,omitempty"`
-	UID               int                 `json:"uid"`
-	GID               int                 `json:"gid"`
-	MaxSessions       int                 `json:"max_sessions"`
-	QuotaSize         int64               `json:"quota_size"`
-	QuotaFiles        int                 `json:"quota_files"`
-	Permissions       map[string][]string `json:"permissions"`
-	UsedQuotaSize     int64               `json:"used_quota_size"`
-	UsedQuotaFiles    int                 `json:"used_quota_files"`
-	LastQuotaUpdate   int64               `json:"last_quota_update"`
-	UploadBandwidth   int64               `json:"upload_bandwidth"`
-	DownloadBandwidth int64               `json:"download_bandwidth"`
-	LastLogin         int64               `json:"last_login"`
-	Filters           UserFilters         `json:"filters"`
-	FsConfig          compatFilesystemV4  `json:"filesystem"`
-}
-
-type backupDataV4Compat struct {
-	Users   []compatUserV4          `json:"users"`
-	Folders []vfs.BaseVirtualFolder `json:"folders"`
-}
-
-func createUserFromV4(u compatUserV4, fsConfig Filesystem) User {
-	user := User{
-		ID:                u.ID,
-		Status:            u.Status,
-		Username:          u.Username,
-		ExpirationDate:    u.ExpirationDate,
-		Password:          u.Password,
-		PublicKeys:        u.PublicKeys,
-		HomeDir:           u.HomeDir,
-		VirtualFolders:    u.VirtualFolders,
-		UID:               u.UID,
-		GID:               u.GID,
-		MaxSessions:       u.MaxSessions,
-		QuotaSize:         u.QuotaSize,
-		QuotaFiles:        u.QuotaFiles,
-		Permissions:       u.Permissions,
-		UsedQuotaSize:     u.UsedQuotaSize,
-		UsedQuotaFiles:    u.UsedQuotaFiles,
-		LastQuotaUpdate:   u.LastQuotaUpdate,
-		UploadBandwidth:   u.UploadBandwidth,
-		DownloadBandwidth: u.DownloadBandwidth,
-		LastLogin:         u.LastLogin,
-		Filters:           u.Filters,
-	}
-	user.FsConfig = fsConfig
-	user.SetEmptySecretsIfNil()
-	return user
-}
-
-func convertUserToV4(u User, fsConfig compatFilesystemV4) compatUserV4 {
-	user := compatUserV4{
-		ID:                u.ID,
-		Status:            u.Status,
-		Username:          u.Username,
-		ExpirationDate:    u.ExpirationDate,
-		Password:          u.Password,
-		PublicKeys:        u.PublicKeys,
-		HomeDir:           u.HomeDir,
-		VirtualFolders:    u.VirtualFolders,
-		UID:               u.UID,
-		GID:               u.GID,
-		MaxSessions:       u.MaxSessions,
-		QuotaSize:         u.QuotaSize,
-		QuotaFiles:        u.QuotaFiles,
-		Permissions:       u.Permissions,
-		UsedQuotaSize:     u.UsedQuotaSize,
-		UsedQuotaFiles:    u.UsedQuotaFiles,
-		LastQuotaUpdate:   u.LastQuotaUpdate,
-		UploadBandwidth:   u.UploadBandwidth,
-		DownloadBandwidth: u.DownloadBandwidth,
-		LastLogin:         u.LastLogin,
-		Filters:           u.Filters,
-	}
-	user.FsConfig = fsConfig
-	return user
-}
-
-func getCGSCredentialsFromV4(config compatGCSFsConfigV4) (*kms.Secret, error) {
-	secret := kms.NewEmptySecret()
-	var err error
-	if len(config.Credentials) > 0 {
-		secret = kms.NewPlainSecret(string(config.Credentials))
-		return secret, nil
-	}
-	if config.CredentialFile != "" {
-		creds, err := ioutil.ReadFile(config.CredentialFile)
-		if err != nil {
-			return secret, err
-		}
-		secret = kms.NewPlainSecret(string(creds))
-		return secret, nil
-	}
-	return secret, err
-}
-
-func getCGSCredentialsFromV6(config vfs.GCSFsConfig, username string) (string, error) {
-	if config.Credentials == nil {
-		config.Credentials = kms.NewEmptySecret()
-	}
-	if config.Credentials.IsEmpty() {
-		config.CredentialFile = filepath.Join(credentialsDirPath, fmt.Sprintf("%v_gcs_credentials.json",
-			username))
-		creds, err := ioutil.ReadFile(config.CredentialFile)
-		if err != nil {
-			return "", err
-		}
-		err = json.Unmarshal(creds, &config.Credentials)
-		if err != nil {
-			return "", err
-		}
-	}
-	if config.Credentials.IsEncrypted() {
-		err := config.Credentials.Decrypt()
-		if err != nil {
-			return "", err
-		}
-		// in V4 GCS credentials were not encrypted
-		return config.Credentials.GetPayload(), nil
-	}
-	return "", nil
-}
-
-func convertFsConfigToV4(fs Filesystem, username string) (compatFilesystemV4, error) {
-	fsV4 := compatFilesystemV4{
-		Provider:     fs.Provider,
-		S3Config:     compatS3FsConfigV4{},
-		AzBlobConfig: compatAzBlobFsConfigV4{},
-		GCSConfig:    compatGCSFsConfigV4{},
-	}
-	switch fs.Provider {
-	case S3FilesystemProvider:
-		fsV4.S3Config = compatS3FsConfigV4{
-			Bucket:            fs.S3Config.Bucket,
-			KeyPrefix:         fs.S3Config.KeyPrefix,
-			Region:            fs.S3Config.Region,
-			AccessKey:         fs.S3Config.AccessKey,
-			AccessSecret:      "",
-			Endpoint:          fs.S3Config.Endpoint,
-			StorageClass:      fs.S3Config.StorageClass,
-			UploadPartSize:    fs.S3Config.UploadPartSize,
-			UploadConcurrency: fs.S3Config.UploadConcurrency,
-		}
-		if fs.S3Config.AccessSecret.IsEncrypted() {
-			err := fs.S3Config.AccessSecret.Decrypt()
-			if err != nil {
-				return fsV4, err
-			}
-			secretV4, err := utils.EncryptData(fs.S3Config.AccessSecret.GetPayload())
-			if err != nil {
-				return fsV4, err
-			}
-			fsV4.S3Config.AccessSecret = secretV4
-		}
-	case AzureBlobFilesystemProvider:
-		fsV4.AzBlobConfig = compatAzBlobFsConfigV4{
-			Container:         fs.AzBlobConfig.Container,
-			AccountName:       fs.AzBlobConfig.AccountName,
-			AccountKey:        "",
-			Endpoint:          fs.AzBlobConfig.Endpoint,
-			SASURL:            fs.AzBlobConfig.SASURL,
-			KeyPrefix:         fs.AzBlobConfig.KeyPrefix,
-			UploadPartSize:    fs.AzBlobConfig.UploadPartSize,
-			UploadConcurrency: fs.AzBlobConfig.UploadConcurrency,
-			UseEmulator:       fs.AzBlobConfig.UseEmulator,
-			AccessTier:        fs.AzBlobConfig.AccessTier,
-		}
-		if fs.AzBlobConfig.AccountKey.IsEncrypted() {
-			err := fs.AzBlobConfig.AccountKey.Decrypt()
-			if err != nil {
-				return fsV4, err
-			}
-			secretV4, err := utils.EncryptData(fs.AzBlobConfig.AccountKey.GetPayload())
-			if err != nil {
-				return fsV4, err
-			}
-			fsV4.AzBlobConfig.AccountKey = secretV4
-		}
-	case GCSFilesystemProvider:
-		fsV4.GCSConfig = compatGCSFsConfigV4{
-			Bucket:               fs.GCSConfig.Bucket,
-			KeyPrefix:            fs.GCSConfig.KeyPrefix,
-			CredentialFile:       fs.GCSConfig.CredentialFile,
-			AutomaticCredentials: fs.GCSConfig.AutomaticCredentials,
-			StorageClass:         fs.GCSConfig.StorageClass,
-		}
-		if fs.GCSConfig.AutomaticCredentials == 0 {
-			creds, err := getCGSCredentialsFromV6(fs.GCSConfig, username)
-			if err != nil {
-				return fsV4, err
-			}
-			fsV4.GCSConfig.Credentials = []byte(creds)
-		}
-	default:
-		// a provider not supported in v4, the configuration will be lost
-		providerLog(logger.LevelWarn, "provider %v was not supported in v4, the configuration for the user %#v will be lost",
-			fs.Provider, username)
-		fsV4.Provider = 0
-	}
-	return fsV4, nil
-}
-
-func convertFsConfigFromV4(compatFs compatFilesystemV4, username string) (Filesystem, error) {
-	fsConfig := Filesystem{
-		Provider:     compatFs.Provider,
-		S3Config:     vfs.S3FsConfig{},
-		AzBlobConfig: vfs.AzBlobFsConfig{},
-		GCSConfig:    vfs.GCSFsConfig{},
-	}
-	switch compatFs.Provider {
-	case S3FilesystemProvider:
-		fsConfig.S3Config = vfs.S3FsConfig{
-			Bucket:            compatFs.S3Config.Bucket,
-			KeyPrefix:         compatFs.S3Config.KeyPrefix,
-			Region:            compatFs.S3Config.Region,
-			AccessKey:         compatFs.S3Config.AccessKey,
-			AccessSecret:      kms.NewEmptySecret(),
-			Endpoint:          compatFs.S3Config.Endpoint,
-			StorageClass:      compatFs.S3Config.StorageClass,
-			UploadPartSize:    compatFs.S3Config.UploadPartSize,
-			UploadConcurrency: compatFs.S3Config.UploadConcurrency,
-		}
-		if compatFs.S3Config.AccessSecret != "" {
-			secret, err := kms.GetSecretFromCompatString(compatFs.S3Config.AccessSecret)
-			if err != nil {
-				providerLog(logger.LevelError, "unable to convert v4 filesystem for user %#v: %v", username, err)
-				return fsConfig, err
-			}
-			fsConfig.S3Config.AccessSecret = secret
-		}
-	case AzureBlobFilesystemProvider:
-		fsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
-			Container:         compatFs.AzBlobConfig.Container,
-			AccountName:       compatFs.AzBlobConfig.AccountName,
-			AccountKey:        kms.NewEmptySecret(),
-			Endpoint:          compatFs.AzBlobConfig.Endpoint,
-			SASURL:            compatFs.AzBlobConfig.SASURL,
-			KeyPrefix:         compatFs.AzBlobConfig.KeyPrefix,
-			UploadPartSize:    compatFs.AzBlobConfig.UploadPartSize,
-			UploadConcurrency: compatFs.AzBlobConfig.UploadConcurrency,
-			UseEmulator:       compatFs.AzBlobConfig.UseEmulator,
-			AccessTier:        compatFs.AzBlobConfig.AccessTier,
-		}
-		if compatFs.AzBlobConfig.AccountKey != "" {
-			secret, err := kms.GetSecretFromCompatString(compatFs.AzBlobConfig.AccountKey)
-			if err != nil {
-				providerLog(logger.LevelError, "unable to convert v4 filesystem for user %#v: %v", username, err)
-				return fsConfig, err
-			}
-			fsConfig.AzBlobConfig.AccountKey = secret
-		}
-	case GCSFilesystemProvider:
-		fsConfig.GCSConfig = vfs.GCSFsConfig{
-			Bucket:               compatFs.GCSConfig.Bucket,
-			KeyPrefix:            compatFs.GCSConfig.KeyPrefix,
-			CredentialFile:       compatFs.GCSConfig.CredentialFile,
-			AutomaticCredentials: compatFs.GCSConfig.AutomaticCredentials,
-			StorageClass:         compatFs.GCSConfig.StorageClass,
-		}
-		if compatFs.GCSConfig.AutomaticCredentials == 0 {
-			compatFs.GCSConfig.CredentialFile = filepath.Join(credentialsDirPath, fmt.Sprintf("%v_gcs_credentials.json",
-				username))
-		}
-		secret, err := getCGSCredentialsFromV4(compatFs.GCSConfig)
-		if err != nil {
-			providerLog(logger.LevelError, "unable to convert v4 filesystem for user %#v: %v", username, err)
-			return fsConfig, err
-		}
-		fsConfig.GCSConfig.Credentials = secret
-	}
-	return fsConfig, nil
-}

+ 1 - 75
dataprovider/dataprovider.go

@@ -104,7 +104,7 @@ var (
 	// ValidProtocols defines all the valid protcols
 	ValidProtocols = []string{"SSH", "FTP", "DAV"}
 	// ErrNoInitRequired defines the error returned by InitProvider if no inizialization/update is required
-	ErrNoInitRequired = errors.New("The data provider is already up to date")
+	ErrNoInitRequired = errors.New("The data provider is up to date")
 	// ErrInvalidCredentials defines the error to return if the supplied credentials are invalid
 	ErrInvalidCredentials = errors.New("invalid credentials")
 	webDAVUsersCache      sync.Map
@@ -291,46 +291,6 @@ func (d *BackupData) HasFolder(name string) bool {
 	return false
 }
 
-func (d *BackupData) checkFolderNames() {
-	if len(d.Folders) == 0 {
-		return
-	}
-	if d.Folders[0].Name != "" {
-		return
-	}
-	logger.WarnToConsole("You are loading folders without a name, please update to the latest supported format. This compatibility layer will be removed soon.")
-	providerLog(logger.LevelWarn, "You are loading folders without a name, please update to the latest supported format. This compatibility layer will be removed soon.")
-	folders := make([]vfs.BaseVirtualFolder, 0, len(d.Folders))
-	for idx, folder := range d.Folders {
-		if folder.Name == "" {
-			folder.Name = fmt.Sprintf("Folder%v", idx)
-		}
-		folders = append(folders, folder)
-	}
-	d.Folders = folders
-	users := make([]User, 0, len(d.Users))
-	for _, user := range d.Users {
-		if len(user.VirtualFolders) > 0 {
-			vfolders := make([]vfs.VirtualFolder, 0, len(user.VirtualFolders))
-			for _, vfolder := range user.VirtualFolders {
-				if vfolder.Name == "" {
-					for _, f := range d.Folders {
-						if f.MappedPath == vfolder.MappedPath {
-							vfolder.Name = f.Name
-						}
-					}
-				}
-				if vfolder.Name != "" {
-					vfolders = append(vfolders, vfolder)
-				}
-			}
-			user.VirtualFolders = vfolders
-		}
-		users = append(users, user)
-	}
-	d.Users = users
-}
-
 type keyboardAuthHookRequest struct {
 	RequestID string   `json:"request_id"`
 	Username  string   `json:"username,omitempty"`
@@ -363,18 +323,6 @@ type checkPasswordResponse struct {
 	ToVerify string `json:"to_verify"`
 }
 
-type virtualFoldersCompact struct {
-	VirtualPath      string `json:"virtual_path"`
-	MappedPath       string `json:"mapped_path"`
-	ExcludeFromQuota bool   `json:"exclude_from_quota"`
-}
-
-type userCompactVFolders struct {
-	ID             int64                   `json:"id"`
-	Username       string                  `json:"username"`
-	VirtualFolders []virtualFoldersCompact `json:"virtual_folders"`
-}
-
 // ValidationError raised if input data is not valid
 type ValidationError struct {
 	err string
@@ -864,28 +812,6 @@ func DumpData() (BackupData, error) {
 func ParseDumpData(data []byte) (BackupData, error) {
 	var dump BackupData
 	err := json.Unmarshal(data, &dump)
-	if err == nil {
-		dump.checkFolderNames()
-		return dump, err
-	}
-	dump = BackupData{}
-	// try to parse as version 4
-	var dumpCompat backupDataV4Compat
-	err = json.Unmarshal(data, &dumpCompat)
-	if err != nil {
-		return dump, err
-	}
-	logger.WarnToConsole("You are loading data from an old format, please update to the latest supported one. We only support the current and the previous format.")
-	providerLog(logger.LevelWarn, "You are loading data from an old format, please update to the latest supported one. We only support the current and the previous format.")
-	dump.Folders = dumpCompat.Folders
-	for _, compatUser := range dumpCompat.Users {
-		fsConfig, err := convertFsConfigFromV4(compatUser.FsConfig, compatUser.Username)
-		if err != nil {
-			return dump, err
-		}
-		dump.Users = append(dump.Users, createUserFromV4(compatUser, fsConfig))
-	}
-	dump.checkFolderNames()
 	return dump, err
 }
 

+ 38 - 231
dataprovider/mysql.go

@@ -5,6 +5,7 @@ package dataprovider
 import (
 	"context"
 	"database/sql"
+	"errors"
 	"fmt"
 	"strings"
 	"time"
@@ -18,41 +19,26 @@ import (
 )
 
 const (
-	mysqlUsersTableSQL = "CREATE TABLE `{{users}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, " +
-		"`username` varchar(255) NOT NULL UNIQUE, `password` varchar(255) NULL, `public_keys` longtext NULL, " +
-		"`home_dir` varchar(255) NOT NULL, `uid` integer NOT NULL, `gid` integer NOT NULL, `max_sessions` integer NOT NULL, " +
-		" `quota_size` bigint NOT NULL, `quota_files` integer NOT NULL, `permissions` longtext NOT NULL, " +
-		"`used_quota_size` bigint NOT NULL, `used_quota_files` integer NOT NULL, `last_quota_update` bigint NOT NULL, " +
-		"`upload_bandwidth` integer NOT NULL, `download_bandwidth` integer NOT NULL, `expiration_date` bigint(20) NOT NULL, " +
-		"`last_login` bigint(20) NOT NULL, `status` int(11) NOT NULL, `filters` longtext DEFAULT NULL, " +
-		"`filesystem` longtext DEFAULT NULL);"
-	mysqlSchemaTableSQL = "CREATE TABLE `{{schema_version}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `version` integer NOT NULL);"
-	mysqlV2SQL          = "ALTER TABLE `{{users}}` ADD COLUMN `virtual_folders` longtext NULL;"
-	mysqlV3SQL          = "ALTER TABLE `{{users}}` MODIFY `password` longtext NULL;"
-	mysqlV4SQL          = "CREATE TABLE `{{folders}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `path` varchar(512) NOT NULL UNIQUE," +
-		"`used_quota_size` bigint NOT NULL, `used_quota_files` integer NOT NULL, `last_quota_update` bigint NOT NULL);" +
-		"ALTER TABLE `{{users}}` MODIFY `home_dir` varchar(512) NOT NULL;" +
-		"ALTER TABLE `{{users}}` DROP COLUMN `virtual_folders`;" +
+	mysqlInitialSQL = "CREATE TABLE `{{schema_version}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `version` integer NOT NULL);" +
+		"CREATE TABLE `{{admins}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `username` varchar(255) NOT NULL UNIQUE, " +
+		"`password` varchar(255) NOT NULL, `email` varchar(255) NULL, `status` integer NOT NULL, `permissions` longtext NOT NULL, " +
+		"`filters` longtext NULL, `additional_info` longtext NULL);" +
+		"CREATE TABLE `{{folders}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(255) NOT NULL UNIQUE, " +
+		"`path` varchar(512) NULL, `used_quota_size` bigint NOT NULL, `used_quota_files` integer NOT NULL, " +
+		"`last_quota_update` bigint NOT NULL);" +
+		"CREATE TABLE `{{users}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `status` integer NOT NULL, " +
+		"`expiration_date` bigint NOT NULL, `username` varchar(255) NOT NULL UNIQUE, `password` longtext NULL, " +
+		"`public_keys` longtext NULL, `home_dir` varchar(512) NOT NULL, `uid` integer NOT NULL, `gid` integer NOT NULL, " +
+		"`max_sessions` integer NOT NULL, `quota_size` bigint NOT NULL, `quota_files` integer NOT NULL, " +
+		"`permissions` longtext NOT NULL, `used_quota_size` bigint NOT NULL, `used_quota_files` integer NOT NULL, " +
+		"`last_quota_update` bigint NOT NULL, `upload_bandwidth` integer NOT NULL, `download_bandwidth` integer NOT NULL, " +
+		"`last_login` bigint NOT NULL, `filters` longtext NULL, `filesystem` longtext NULL, `additional_info` longtext NULL);" +
 		"CREATE TABLE `{{folders_mapping}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `virtual_path` varchar(512) NOT NULL, " +
 		"`quota_size` bigint NOT NULL, `quota_files` integer NOT NULL, `folder_id` integer NOT NULL, `user_id` integer NOT NULL);" +
 		"ALTER TABLE `{{folders_mapping}}` ADD CONSTRAINT `unique_mapping` UNIQUE (`user_id`, `folder_id`);" +
 		"ALTER TABLE `{{folders_mapping}}` ADD CONSTRAINT `folders_mapping_folder_id_fk_folders_id` FOREIGN KEY (`folder_id`) REFERENCES `{{folders}}` (`id`) ON DELETE CASCADE;" +
-		"ALTER TABLE `{{folders_mapping}}` ADD CONSTRAINT `folders_mapping_user_id_fk_users_id` FOREIGN KEY (`user_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE;"
-	mysqlV6SQL     = "ALTER TABLE `{{users}}` ADD COLUMN `additional_info` longtext NULL;"
-	mysqlV6DownSQL = "ALTER TABLE `{{users}}` DROP COLUMN `additional_info`;"
-	mysqlV7SQL     = "CREATE TABLE `{{admins}}` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `username` varchar(255) NOT NULL UNIQUE, " +
-		"`password` varchar(255) NOT NULL, `email` varchar(255) NULL, `status` integer NOT NULL, `permissions` longtext NOT NULL, " +
-		"`filters` longtext NULL, `additional_info` longtext NULL);"
-	mysqlV7DownSQL = "DROP TABLE `{{admins}}` CASCADE;"
-	mysqlV8SQL     = "ALTER TABLE `{{folders}}` ADD COLUMN `name` varchar(255) NULL;" +
-		"ALTER TABLE `{{folders}}` MODIFY `path` varchar(512) NULL;" +
-		"ALTER TABLE `{{folders}}` DROP INDEX `path`;" +
-		"UPDATE `{{folders}}` f1 SET name = CONCAT('folder',f1.id);" +
-		"ALTER TABLE `{{folders}}` MODIFY `name` varchar(255) NOT NULL;" +
-		"ALTER TABLE `{{folders}}` ADD CONSTRAINT `name` UNIQUE (`name`);"
-	mysqlV8DownSQL = "ALTER TABLE `{{folders}}` DROP COLUMN `name`;" +
-		"ALTER TABLE `{{folders}}` MODIFY `path` varchar(512) NOT NULL;" +
-		"ALTER TABLE `{{folders}}` ADD CONSTRAINT `path` UNIQUE (`path`);"
+		"ALTER TABLE `{{folders_mapping}}` ADD CONSTRAINT `folders_mapping_user_id_fk_users_id` FOREIGN KEY (`user_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE;" +
+		"INSERT INTO {{schema_version}} (version) VALUES (8);"
 )
 
 // MySQLProvider auth provider for MySQL/MariaDB database
@@ -224,27 +210,13 @@ func (p *MySQLProvider) initializeDatabase() error {
 	if err == nil && dbVersion.Version > 0 {
 		return ErrNoInitRequired
 	}
-	sqlUsers := strings.Replace(mysqlUsersTableSQL, "{{users}}", sqlTableUsers, 1)
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
+	initialSQL := strings.ReplaceAll(mysqlInitialSQL, "{{schema_version}}", sqlTableSchemaVersion)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{admins}}", sqlTableAdmins)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{folders}}", sqlTableFolders)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{users}}", sqlTableUsers)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{folders_mapping}}", sqlTableFoldersMapping)
 
-	tx, err := p.dbHandle.BeginTx(ctx, nil)
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(sqlUsers)
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(strings.Replace(mysqlSchemaTableSQL, "{{schema_version}}", sqlTableSchemaVersion, 1))
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(strings.Replace(initialDBVersionSQL, "{{schema_version}}", sqlTableSchemaVersion, 1))
-	if err != nil {
-		return err
-	}
-	return tx.Commit()
+	return sqlCommonExecSQLAndUpdateDBVersion(p.dbHandle, strings.Split(initialSQL, ";"), 8)
 }
 
 func (p *MySQLProvider) migrateDatabase() error {
@@ -252,200 +224,35 @@ func (p *MySQLProvider) migrateDatabase() error {
 	if err != nil {
 		return err
 	}
-	if dbVersion.Version == sqlDatabaseVersion {
-		providerLog(logger.LevelDebug, "sql database is up to date, current version: %v", dbVersion.Version)
+
+	switch version := dbVersion.Version; {
+	case version == sqlDatabaseVersion:
+		providerLog(logger.LevelDebug, "sql database is up to date, current version: %v", version)
 		return ErrNoInitRequired
-	}
-	switch dbVersion.Version {
-	case 1:
-		return updateMySQLDatabaseFromV1(p.dbHandle)
-	case 2:
-		return updateMySQLDatabaseFromV2(p.dbHandle)
-	case 3:
-		return updateMySQLDatabaseFromV3(p.dbHandle)
-	case 4:
-		return updateMySQLDatabaseFromV4(p.dbHandle)
-	case 5:
-		return updateMySQLDatabaseFromV5(p.dbHandle)
-	case 6:
-		return updateMySQLDatabaseFromV6(p.dbHandle)
-	case 7:
-		return updateMySQLDatabaseFromV7(p.dbHandle)
+	case version < 8:
+		err = fmt.Errorf("database version %v is too old, please see the upgrading docs", version)
+		providerLog(logger.LevelError, "%v", err)
+		logger.ErrorToConsole("%v", err)
+		return err
 	default:
-		if dbVersion.Version > sqlDatabaseVersion {
-			providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
+		if version > sqlDatabaseVersion {
+			providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
 				sqlDatabaseVersion)
-			logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
+			logger.WarnToConsole("database version %v is newer than the supported one: %v", version,
 				sqlDatabaseVersion)
 			return nil
 		}
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
+		return fmt.Errorf("Database version not handled: %v", version)
 	}
 }
 
-//nolint:dupl
 func (p *MySQLProvider) revertDatabase(targetVersion int) error {
 	dbVersion, err := sqlCommonGetDatabaseVersion(p.dbHandle, true)
 	if err != nil {
 		return err
 	}
 	if dbVersion.Version == targetVersion {
-		return fmt.Errorf("current version match target version, nothing to do")
-	}
-	switch dbVersion.Version {
-	case 8:
-		err = downgradeMySQLDatabaseFrom8To7(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradeMySQLDatabaseFrom7To6(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradeMySQLDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeMySQLDatabaseFrom5To4(p.dbHandle)
-	case 7:
-		err = downgradeMySQLDatabaseFrom7To6(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradeMySQLDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeMySQLDatabaseFrom5To4(p.dbHandle)
-	case 6:
-		err = downgradeMySQLDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeMySQLDatabaseFrom5To4(p.dbHandle)
-	case 5:
-		return downgradeMySQLDatabaseFrom5To4(p.dbHandle)
-	default:
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
-	}
-}
-
-func updateMySQLDatabaseFromV1(dbHandle *sql.DB) error {
-	err := updateMySQLDatabaseFrom1To2(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateMySQLDatabaseFromV2(dbHandle)
-}
-
-func updateMySQLDatabaseFromV2(dbHandle *sql.DB) error {
-	err := updateMySQLDatabaseFrom2To3(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateMySQLDatabaseFromV3(dbHandle)
-}
-
-func updateMySQLDatabaseFromV3(dbHandle *sql.DB) error {
-	err := updateMySQLDatabaseFrom3To4(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateMySQLDatabaseFromV4(dbHandle)
-}
-
-func updateMySQLDatabaseFromV4(dbHandle *sql.DB) error {
-	err := updateMySQLDatabaseFrom4To5(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateMySQLDatabaseFromV5(dbHandle)
-}
-
-func updateMySQLDatabaseFromV5(dbHandle *sql.DB) error {
-	err := updateMySQLDatabaseFrom5To6(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateMySQLDatabaseFromV6(dbHandle)
-}
-
-func updateMySQLDatabaseFromV6(dbHandle *sql.DB) error {
-	err := updateMySQLDatabaseFrom6To7(dbHandle)
-	if err != nil {
-		return err
+		return errors.New("current version match target version, nothing to do")
 	}
-	return updateMySQLDatabaseFromV7(dbHandle)
-}
-
-func updateMySQLDatabaseFromV7(dbHandle *sql.DB) error {
-	return updateMySQLDatabaseFrom7To8(dbHandle)
-}
-
-func updateMySQLDatabaseFrom1To2(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 1 -> 2")
-	providerLog(logger.LevelInfo, "updating database version: 1 -> 2")
-	sql := strings.Replace(mysqlV2SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 2)
-}
-
-func updateMySQLDatabaseFrom2To3(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 2 -> 3")
-	providerLog(logger.LevelInfo, "updating database version: 2 -> 3")
-	sql := strings.Replace(mysqlV3SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 3)
-}
-
-func updateMySQLDatabaseFrom3To4(dbHandle *sql.DB) error {
-	return sqlCommonUpdateDatabaseFrom3To4(mysqlV4SQL, dbHandle)
-}
-
-func updateMySQLDatabaseFrom4To5(dbHandle *sql.DB) error {
-	return sqlCommonUpdateDatabaseFrom4To5(dbHandle)
-}
-
-func updateMySQLDatabaseFrom5To6(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 5 -> 6")
-	providerLog(logger.LevelInfo, "updating database version: 5 -> 6")
-	sql := strings.Replace(mysqlV6SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
-}
-
-func updateMySQLDatabaseFrom6To7(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 6 -> 7")
-	providerLog(logger.LevelInfo, "updating database version: 6 -> 7")
-	sql := strings.Replace(mysqlV7SQL, "{{admins}}", sqlTableAdmins, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 7)
-}
-
-func updateMySQLDatabaseFrom7To8(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 7 -> 8")
-	providerLog(logger.LevelInfo, "updating database version: 7 -> 8")
-	sql := strings.ReplaceAll(mysqlV8SQL, "{{folders}}", sqlTableFolders)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 8)
-}
-
-func downgradeMySQLDatabaseFrom8To7(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 8 -> 7")
-	providerLog(logger.LevelInfo, "downgrading database version: 8 -> 7")
-	sql := strings.ReplaceAll(mysqlV8DownSQL, "{{folders}}", sqlTableFolders)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 7)
-}
-
-func downgradeMySQLDatabaseFrom7To6(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 7 -> 6")
-	providerLog(logger.LevelInfo, "downgrading database version: 7 -> 6")
-	sql := strings.Replace(mysqlV7DownSQL, "{{admins}}", sqlTableAdmins, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
-}
-
-func downgradeMySQLDatabaseFrom6To5(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 6 -> 5")
-	providerLog(logger.LevelInfo, "downgrading database version: 6 -> 5")
-	sql := strings.Replace(mysqlV6DownSQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 5)
-}
-
-func downgradeMySQLDatabaseFrom5To4(dbHandle *sql.DB) error {
-	return sqlCommonDowngradeDatabaseFrom5To4(dbHandle)
+	return errors.New("the current version cannot be reverted")
 }

+ 43 - 234
dataprovider/pgsql.go

@@ -5,6 +5,7 @@ package dataprovider
 import (
 	"context"
 	"database/sql"
+	"errors"
 	"fmt"
 	"strings"
 	"time"
@@ -18,43 +19,30 @@ import (
 )
 
 const (
-	pgsqlUsersTableSQL = `CREATE TABLE "{{users}}" ("id" serial NOT NULL PRIMARY KEY, "username" varchar(255) NOT NULL UNIQUE,
-"password" varchar(255) NULL, "public_keys" text NULL, "home_dir" varchar(255) NOT NULL, "uid" integer NOT NULL,
-"gid" integer NOT NULL, "max_sessions" integer NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL,
-"permissions" text NOT NULL, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL,
-"last_quota_update" bigint NOT NULL, "upload_bandwidth" integer NOT NULL, "download_bandwidth" integer NOT NULL,
-"expiration_date" bigint NOT NULL, "last_login" bigint NOT NULL, "status" integer NOT NULL, "filters" text NULL,
-"filesystem" text NULL);`
-	pgsqlSchemaTableSQL = `CREATE TABLE "{{schema_version}}" ("id" serial NOT NULL PRIMARY KEY, "version" integer NOT NULL);`
-	pgsqlV2SQL          = `ALTER TABLE "{{users}}" ADD COLUMN "virtual_folders" text NULL;`
-	pgsqlV3SQL          = `ALTER TABLE "{{users}}" ALTER COLUMN "password" TYPE text USING "password"::text;`
-	pgsqlV4SQL          = `CREATE TABLE "{{folders}}" ("id" serial NOT NULL PRIMARY KEY, "path" varchar(512) NOT NULL UNIQUE, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL);
-ALTER TABLE "{{users}}" ALTER COLUMN "home_dir" TYPE varchar(512) USING "home_dir"::varchar(512);
-ALTER TABLE "{{users}}" DROP COLUMN "virtual_folders" CASCADE;
-CREATE TABLE "{{folders_mapping}}" ("id" serial NOT NULL PRIMARY KEY, "virtual_path" varchar(512) NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL, "folder_id" integer NOT NULL, "user_id" integer NOT NULL);
+	pgsqlInitial = `CREATE TABLE "{{schema_version}}" ("id" serial NOT NULL PRIMARY KEY, "version" integer NOT NULL);
+CREATE TABLE "{{admins}}" ("id" serial NOT NULL PRIMARY KEY, "username" varchar(255) NOT NULL UNIQUE,
+"password" varchar(255) NOT NULL, "email" varchar(255) NULL, "status" integer NOT NULL, "permissions" text NOT NULL,
+"filters" text NULL, "additional_info" text NULL);
+CREATE TABLE "{{folders}}" ("id" serial NOT NULL PRIMARY KEY, "name" varchar(255) NOT NULL UNIQUE,
+"path" varchar(512) NULL, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL,
+"last_quota_update" bigint NOT NULL);
+CREATE TABLE "{{users}}" ("id" serial NOT NULL PRIMARY KEY, "status" integer NOT NULL, "expiration_date" bigint NOT NULL,
+"username" varchar(255) NOT NULL UNIQUE, "password" text NULL, "public_keys" text NULL, "home_dir" varchar(512) NOT NULL,
+"uid" integer NOT NULL, "gid" integer NOT NULL, "max_sessions" integer NOT NULL, "quota_size" bigint NOT NULL,
+"quota_files" integer NOT NULL, "permissions" text NOT NULL, "used_quota_size" bigint NOT NULL,
+"used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL, "upload_bandwidth" integer NOT NULL,
+"download_bandwidth" integer NOT NULL, "last_login" bigint NOT NULL, "filters" text NULL, "filesystem" text NULL,
+"additional_info" text NULL);
+CREATE TABLE "{{folders_mapping}}" ("id" serial NOT NULL PRIMARY KEY, "virtual_path" varchar(512) NOT NULL,
+"quota_size" bigint NOT NULL, "quota_files" integer NOT NULL, "folder_id" integer NOT NULL, "user_id" integer NOT NULL);
 ALTER TABLE "{{folders_mapping}}" ADD CONSTRAINT "unique_mapping" UNIQUE ("user_id", "folder_id");
-ALTER TABLE "{{folders_mapping}}" ADD CONSTRAINT "folders_mapping_folder_id_fk_folders_id" FOREIGN KEY ("folder_id") REFERENCES "{{folders}}" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE "{{folders_mapping}}" ADD CONSTRAINT "folders_mapping_user_id_fk_users_id" FOREIGN KEY ("user_id") REFERENCES "{{users}}" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE "{{folders_mapping}}" ADD CONSTRAINT "folders_mapping_folder_id_fk_folders_id"
+FOREIGN KEY ("folder_id") REFERENCES "{{folders}}" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE "{{folders_mapping}}" ADD CONSTRAINT "folders_mapping_user_id_fk_users_id"
+FOREIGN KEY ("user_id") REFERENCES "{{users}}" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 CREATE INDEX "folders_mapping_folder_id_idx" ON "{{folders_mapping}}" ("folder_id");
 CREATE INDEX "folders_mapping_user_id_idx" ON "{{folders_mapping}}" ("user_id");
-`
-	pgsqlV6SQL     = `ALTER TABLE "{{users}}" ADD COLUMN "additional_info" text NULL;`
-	pgsqlV6DownSQL = `ALTER TABLE "{{users}}" DROP COLUMN "additional_info" CASCADE;`
-	pgsqlV7SQL     = `CREATE TABLE "{{admins}}" ("id" serial NOT NULL PRIMARY KEY, "username" varchar(255) NOT NULL UNIQUE,
-"password" varchar(255) NOT NULL, "email" varchar(255) NULL, "status" integer NOT NULL, "permissions" text NOT NULL,
-"filters" text NULL, "additional_info" text NULL);
-`
-	pgsqlV7DownSQL = `DROP TABLE "{{admins}}" CASCADE;`
-	pgsqlV8SQL     = `ALTER TABLE "{{folders}}" ADD COLUMN "name" varchar(255) NULL;
-ALTER TABLE "folders" ALTER COLUMN "path" DROP NOT NULL;
-ALTER TABLE "{{folders}}" DROP CONSTRAINT IF EXISTS folders_path_key;
-UPDATE "{{folders}}" f1 SET name = (SELECT CONCAT('folder',f2.id) FROM "{{folders}}" f2 WHERE f2.id = f1.id);
-ALTER TABLE "{{folders}}" ALTER COLUMN "name" SET NOT NULL;
-ALTER TABLE "{{folders}}" ADD CONSTRAINT "folders_name_uniq" UNIQUE ("name");
-`
-	pgsqlV8DownSQL = `ALTER TABLE "{{folders}}" DROP COLUMN "name" CASCADE;
-ALTER TABLE "{{folders}}" ALTER COLUMN "path" SET NOT NULL;
-ALTER TABLE "{{folders}}" ADD CONSTRAINT folders_path_key UNIQUE (path);
+INSERT INTO {{schema_version}} (version) VALUES (8);
 `
 )
 
@@ -228,27 +216,13 @@ func (p *PGSQLProvider) initializeDatabase() error {
 	if err == nil && dbVersion.Version > 0 {
 		return ErrNoInitRequired
 	}
-	sqlUsers := strings.Replace(pgsqlUsersTableSQL, "{{users}}", sqlTableUsers, 1)
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
+	initialSQL := strings.ReplaceAll(pgsqlInitial, "{{schema_version}}", sqlTableSchemaVersion)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{admins}}", sqlTableAdmins)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{folders}}", sqlTableFolders)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{users}}", sqlTableUsers)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{folders_mapping}}", sqlTableFoldersMapping)
 
-	tx, err := p.dbHandle.BeginTx(ctx, nil)
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(sqlUsers)
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(strings.Replace(pgsqlSchemaTableSQL, "{{schema_version}}", sqlTableSchemaVersion, 1))
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(strings.Replace(initialDBVersionSQL, "{{schema_version}}", sqlTableSchemaVersion, 1))
-	if err != nil {
-		return err
-	}
-	return tx.Commit()
+	return sqlCommonExecSQLAndUpdateDBVersion(p.dbHandle, []string{initialSQL}, 8)
 }
 
 func (p *PGSQLProvider) migrateDatabase() error {
@@ -256,200 +230,35 @@ func (p *PGSQLProvider) migrateDatabase() error {
 	if err != nil {
 		return err
 	}
-	if dbVersion.Version == sqlDatabaseVersion {
-		providerLog(logger.LevelDebug, "sql database is up to date, current version: %v", dbVersion.Version)
+
+	switch version := dbVersion.Version; {
+	case version == sqlDatabaseVersion:
+		providerLog(logger.LevelDebug, "sql database is up to date, current version: %v", version)
 		return ErrNoInitRequired
-	}
-	switch dbVersion.Version {
-	case 1:
-		return updatePGSQLDatabaseFromV1(p.dbHandle)
-	case 2:
-		return updatePGSQLDatabaseFromV2(p.dbHandle)
-	case 3:
-		return updatePGSQLDatabaseFromV3(p.dbHandle)
-	case 4:
-		return updatePGSQLDatabaseFromV4(p.dbHandle)
-	case 5:
-		return updatePGSQLDatabaseFromV5(p.dbHandle)
-	case 6:
-		return updatePGSQLDatabaseFromV6(p.dbHandle)
-	case 7:
-		return updatePGSQLDatabaseFromV7(p.dbHandle)
+	case version < 8:
+		err = fmt.Errorf("database version %v is too old, please see the upgrading docs", version)
+		providerLog(logger.LevelError, "%v", err)
+		logger.ErrorToConsole("%v", err)
+		return err
 	default:
-		if dbVersion.Version > sqlDatabaseVersion {
-			providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
+		if version > sqlDatabaseVersion {
+			providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
 				sqlDatabaseVersion)
-			logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
+			logger.WarnToConsole("database version %v is newer than the supported one: %v", version,
 				sqlDatabaseVersion)
 			return nil
 		}
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
+		return fmt.Errorf("Database version not handled: %v", version)
 	}
 }
 
-//nolint:dupl
 func (p *PGSQLProvider) revertDatabase(targetVersion int) error {
 	dbVersion, err := sqlCommonGetDatabaseVersion(p.dbHandle, true)
 	if err != nil {
 		return err
 	}
 	if dbVersion.Version == targetVersion {
-		return fmt.Errorf("current version match target version, nothing to do")
+		return errors.New("current version match target version, nothing to do")
 	}
-	switch dbVersion.Version {
-	case 8:
-		err = downgradePGSQLDatabaseFrom8To7(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradePGSQLDatabaseFrom7To6(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradePGSQLDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradePGSQLDatabaseFrom5To4(p.dbHandle)
-	case 7:
-		err = downgradePGSQLDatabaseFrom7To6(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradePGSQLDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradePGSQLDatabaseFrom5To4(p.dbHandle)
-	case 6:
-		err = downgradePGSQLDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradePGSQLDatabaseFrom5To4(p.dbHandle)
-	case 5:
-		return downgradePGSQLDatabaseFrom5To4(p.dbHandle)
-	default:
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
-	}
-}
-
-func updatePGSQLDatabaseFromV1(dbHandle *sql.DB) error {
-	err := updatePGSQLDatabaseFrom1To2(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updatePGSQLDatabaseFromV2(dbHandle)
-}
-
-func updatePGSQLDatabaseFromV2(dbHandle *sql.DB) error {
-	err := updatePGSQLDatabaseFrom2To3(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updatePGSQLDatabaseFromV3(dbHandle)
-}
-
-func updatePGSQLDatabaseFromV3(dbHandle *sql.DB) error {
-	err := updatePGSQLDatabaseFrom3To4(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updatePGSQLDatabaseFromV4(dbHandle)
-}
-
-func updatePGSQLDatabaseFromV4(dbHandle *sql.DB) error {
-	err := updatePGSQLDatabaseFrom4To5(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updatePGSQLDatabaseFromV5(dbHandle)
-}
-
-func updatePGSQLDatabaseFromV5(dbHandle *sql.DB) error {
-	err := updatePGSQLDatabaseFrom5To6(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updatePGSQLDatabaseFromV6(dbHandle)
-}
-
-func updatePGSQLDatabaseFromV6(dbHandle *sql.DB) error {
-	err := updatePGSQLDatabaseFrom6To7(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updatePGSQLDatabaseFromV7(dbHandle)
-}
-
-func updatePGSQLDatabaseFromV7(dbHandle *sql.DB) error {
-	return updatePGSQLDatabaseFrom7To8(dbHandle)
-}
-
-func updatePGSQLDatabaseFrom1To2(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 1 -> 2")
-	providerLog(logger.LevelInfo, "updating database version: 1 -> 2")
-	sql := strings.Replace(pgsqlV2SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 2)
-}
-
-func updatePGSQLDatabaseFrom2To3(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 2 -> 3")
-	providerLog(logger.LevelInfo, "updating database version: 2 -> 3")
-	sql := strings.Replace(pgsqlV3SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 3)
-}
-
-func updatePGSQLDatabaseFrom3To4(dbHandle *sql.DB) error {
-	return sqlCommonUpdateDatabaseFrom3To4(pgsqlV4SQL, dbHandle)
-}
-
-func updatePGSQLDatabaseFrom4To5(dbHandle *sql.DB) error {
-	return sqlCommonUpdateDatabaseFrom4To5(dbHandle)
-}
-
-func updatePGSQLDatabaseFrom5To6(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 5 -> 6")
-	providerLog(logger.LevelInfo, "updating database version: 5 -> 6")
-	sql := strings.Replace(pgsqlV6SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
-}
-
-func updatePGSQLDatabaseFrom6To7(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 6 -> 7")
-	providerLog(logger.LevelInfo, "updating database version: 6 -> 7")
-	sql := strings.Replace(pgsqlV7SQL, "{{admins}}", sqlTableAdmins, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 7)
-}
-
-func updatePGSQLDatabaseFrom7To8(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 7 -> 8")
-	providerLog(logger.LevelInfo, "updating database version: 7 -> 8")
-	sql := strings.ReplaceAll(pgsqlV8SQL, "{{folders}}", sqlTableFolders)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 8)
-}
-
-func downgradePGSQLDatabaseFrom8To7(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 8 -> 7")
-	providerLog(logger.LevelInfo, "downgrading database version: 8 -> 7")
-	sql := strings.ReplaceAll(pgsqlV8DownSQL, "{{folders}}", sqlTableAdmins)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 7)
-}
-
-func downgradePGSQLDatabaseFrom7To6(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 7 -> 6")
-	providerLog(logger.LevelInfo, "downgrading database version: 7 -> 6")
-	sql := strings.Replace(pgsqlV7DownSQL, "{{admins}}", sqlTableAdmins, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
-}
-
-func downgradePGSQLDatabaseFrom6To5(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 6 -> 5")
-	providerLog(logger.LevelInfo, "downgrading database version: 6 -> 5")
-	sql := strings.Replace(pgsqlV6DownSQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 5)
-}
-
-func downgradePGSQLDatabaseFrom5To4(dbHandle *sql.DB) error {
-	return sqlCommonDowngradeDatabaseFrom5To4(dbHandle)
+	return errors.New("the current version cannot be reverted")
 }

+ 5 - 295
dataprovider/sqlcommon.go

@@ -16,7 +16,6 @@ import (
 
 const (
 	sqlDatabaseVersion     = 8
-	initialDBVersionSQL    = "INSERT INTO {{schema_version}} (version) VALUES (1);"
 	defaultSQLQueryTimeout = 10 * time.Second
 	longSQLQueryTimeout    = 60 * time.Second
 )
@@ -482,25 +481,6 @@ func sqlCommonGetUsers(limit int, offset int, order string, dbHandle sqlQuerier)
 	return getUsersWithVirtualFolders(users, dbHandle)
 }
 
-func updateUserPermissionsFromDb(user *User, permissions string) error {
-	var err error
-	perms := make(map[string][]string)
-	err = json.Unmarshal([]byte(permissions), &perms)
-	if err == nil {
-		user.Permissions = perms
-	} else {
-		// compatibility layer: until version 0.9.4 permissions were a string list
-		var list []string
-		err = json.Unmarshal([]byte(permissions), &list)
-		if err != nil {
-			return err
-		}
-		perms["/"] = list
-		user.Permissions = perms
-	}
-	return err
-}
-
 func getAdminFromDbRow(row sqlScanner) (Admin, error) {
 	var admin Admin
 	var email, filters, additionalInfo, permissions sql.NullString
@@ -574,10 +554,13 @@ func getUserFromDbRow(row sqlScanner) (User, error) {
 		}
 	}
 	if permissions.Valid {
-		err = updateUserPermissionsFromDb(&user, permissions.String)
+		perms := make(map[string][]string)
+		err = json.Unmarshal([]byte(permissions.String), &perms)
 		if err != nil {
-			return user, err
+			providerLog(logger.LevelDebug, "unable to deserialize permissions for user %#v: %v", user.Username, err)
+			return user, fmt.Errorf("unable to deserialize permissions for user %#v: %v", user.Username, err)
 		}
+		user.Permissions = perms
 	}
 	if filters.Valid {
 		var userFilters UserFilters
@@ -1020,276 +1003,3 @@ func sqlCommonExecSQLAndUpdateDBVersion(dbHandle *sql.DB, sql []string, newVersi
 	}
 	return tx.Commit()
 }
-
-/*func sqlCommonGetCompatVirtualFolders(dbHandle *sql.DB) ([]userCompactVFolders, error) {
-	users := []userCompactVFolders{}
-	ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
-	defer cancel()
-	q := getCompatVirtualFoldersQuery()
-	stmt, err := dbHandle.PrepareContext(ctx, q)
-	if err != nil {
-		providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
-		return nil, err
-	}
-	defer stmt.Close()
-	rows, err := stmt.QueryContext(ctx)
-	if err != nil {
-		return nil, err
-	}
-	defer rows.Close()
-	for rows.Next() {
-		var user userCompactVFolders
-		var virtualFolders sql.NullString
-		err = rows.Scan(&user.ID, &user.Username, &virtualFolders)
-		if err != nil {
-			return nil, err
-		}
-		if virtualFolders.Valid {
-			var list []virtualFoldersCompact
-			err = json.Unmarshal([]byte(virtualFolders.String), &list)
-			if err == nil && len(list) > 0 {
-				user.VirtualFolders = list
-				users = append(users, user)
-			}
-		}
-	}
-	return users, rows.Err()
-}*/
-
-/*func sqlCommonRestoreCompatVirtualFolders(ctx context.Context, users []userCompactVFolders, dbHandle sqlQuerier) ([]string, error) {
-	foldersToScan := []string{}
-	for _, user := range users {
-		for _, vfolder := range user.VirtualFolders {
-			providerLog(logger.LevelInfo, "restoring virtual folder: %+v for user %#v", vfolder, user.Username)
-			// -1 means included in user quota, 0 means unlimited
-			quotaSize := int64(-1)
-			quotaFiles := -1
-			if vfolder.ExcludeFromQuota {
-				quotaFiles = 0
-				quotaSize = 0
-			}
-			b, err := sqlCommonAddOrGetFolder(ctx, vfolder.MappedPath, 0, 0, 0, dbHandle)
-			if err != nil {
-				providerLog(logger.LevelWarn, "error restoring virtual folder for user %#v: %v", user.Username, err)
-				return foldersToScan, err
-			}
-			u := User{
-				ID:       user.ID,
-				Username: user.Username,
-			}
-			f := vfs.VirtualFolder{
-				BaseVirtualFolder: b,
-				VirtualPath:       vfolder.VirtualPath,
-				QuotaSize:         quotaSize,
-				QuotaFiles:        quotaFiles,
-			}
-			err = sqlCommonAddFolderMapping(ctx, &u, f, dbHandle)
-			if err != nil {
-				providerLog(logger.LevelWarn, "error adding virtual folder mapping for user %#v: %v", user.Username, err)
-				return foldersToScan, err
-			}
-			if !utils.IsStringInSlice(vfolder.MappedPath, foldersToScan) {
-				foldersToScan = append(foldersToScan, vfolder.MappedPath)
-			}
-			providerLog(logger.LevelInfo, "virtual folder: %+v for user %#v successfully restored", vfolder, user.Username)
-		}
-	}
-	return foldersToScan, nil
-}*/
-
-func sqlCommonUpdateDatabaseFrom3To4(sqlV4 string, dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 3 -> 4")
-	providerLog(logger.LevelInfo, "updating database version: 3 -> 4")
-	/*users, err := sqlCommonGetCompatVirtualFolders(dbHandle)
-	if err != nil {
-		return err
-	}*/
-	sql := strings.ReplaceAll(sqlV4, "{{users}}", sqlTableUsers)
-	sql = strings.ReplaceAll(sql, "{{folders}}", sqlTableFolders)
-	sql = strings.ReplaceAll(sql, "{{folders_mapping}}", sqlTableFoldersMapping)
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
-
-	tx, err := dbHandle.BeginTx(ctx, nil)
-	if err != nil {
-		return err
-	}
-	for _, q := range strings.Split(sql, ";") {
-		if strings.TrimSpace(q) == "" {
-			continue
-		}
-		_, err = tx.ExecContext(ctx, q)
-		if err != nil {
-			return err
-		}
-	}
-	err = sqlCommonUpdateDatabaseVersion(ctx, tx, 4)
-	if err != nil {
-		return err
-	}
-	return tx.Commit()
-}
-
-//nolint:dupl
-func sqlCommonUpdateDatabaseFrom4To5(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 4 -> 5")
-	providerLog(logger.LevelInfo, "updating database version: 4 -> 5")
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
-	q := getCompatV4FsConfigQuery()
-	stmt, err := dbHandle.PrepareContext(ctx, q)
-	if err != nil {
-		providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
-		return err
-	}
-	defer stmt.Close()
-	rows, err := stmt.QueryContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer rows.Close()
-
-	users := []User{}
-	for rows.Next() {
-		var compatUser compatUserV4
-		var fsConfigString sql.NullString
-		err = rows.Scan(&compatUser.ID, &compatUser.Username, &fsConfigString)
-		if err != nil {
-			return err
-		}
-		if fsConfigString.Valid {
-			err = json.Unmarshal([]byte(fsConfigString.String), &compatUser.FsConfig)
-			if err != nil {
-				logger.WarnToConsole("failed to unmarshal v4 user %#v, is it already migrated?", compatUser.Username)
-				continue
-			}
-			fsConfig, err := convertFsConfigFromV4(compatUser.FsConfig, compatUser.Username)
-			if err != nil {
-				return err
-			}
-			users = append(users, createUserFromV4(compatUser, fsConfig))
-		}
-	}
-	if err := rows.Err(); err != nil {
-		return err
-	}
-
-	for _, user := range users {
-		err = sqlCommonUpdateV4User(dbHandle, user)
-		if err != nil {
-			return err
-		}
-		providerLog(logger.LevelInfo, "filesystem config updated for user %#v", user.Username)
-	}
-
-	ctxVersion, cancelVersion := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
-	defer cancelVersion()
-
-	return sqlCommonUpdateDatabaseVersion(ctxVersion, dbHandle, 5)
-}
-
-func sqlCommonUpdateV4CompatUser(dbHandle *sql.DB, user compatUserV4) error {
-	ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
-	defer cancel()
-
-	q := updateCompatV4FsConfigQuery()
-	stmt, err := dbHandle.PrepareContext(ctx, q)
-	if err != nil {
-		providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
-		return err
-	}
-	defer stmt.Close()
-
-	fsConfig, err := json.Marshal(user.FsConfig)
-	if err != nil {
-		return err
-	}
-	_, err = stmt.ExecContext(ctx, string(fsConfig), user.ID)
-	return err
-}
-
-func sqlCommonUpdateV4User(dbHandle *sql.DB, user User) error {
-	err := validateFilesystemConfig(&user)
-	if err != nil {
-		return err
-	}
-	err = saveGCSCredentials(&user)
-	if err != nil {
-		return err
-	}
-	ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
-	defer cancel()
-
-	q := updateCompatV4FsConfigQuery()
-	stmt, err := dbHandle.PrepareContext(ctx, q)
-	if err != nil {
-		providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
-		return err
-	}
-	defer stmt.Close()
-
-	fsConfig, err := user.GetFsConfigAsJSON()
-	if err != nil {
-		return err
-	}
-	_, err = stmt.ExecContext(ctx, string(fsConfig), user.ID)
-	return err
-}
-
-//nolint:dupl
-func sqlCommonDowngradeDatabaseFrom5To4(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 5 -> 4")
-	providerLog(logger.LevelInfo, "downgrading database version: 5 -> 4")
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
-	q := getCompatV4FsConfigQuery()
-	stmt, err := dbHandle.PrepareContext(ctx, q)
-	if err != nil {
-		providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
-		return err
-	}
-	defer stmt.Close()
-	rows, err := stmt.QueryContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer rows.Close()
-
-	users := []compatUserV4{}
-	for rows.Next() {
-		var user User
-		var fsConfigString sql.NullString
-		err = rows.Scan(&user.ID, &user.Username, &fsConfigString)
-		if err != nil {
-			return err
-		}
-		if fsConfigString.Valid {
-			err = json.Unmarshal([]byte(fsConfigString.String), &user.FsConfig)
-			if err != nil {
-				logger.WarnToConsole("failed to unmarshal user %#v to v4, is it already migrated?", user.Username)
-				continue
-			}
-			fsConfig, err := convertFsConfigToV4(user.FsConfig, user.Username)
-			if err != nil {
-				return err
-			}
-			users = append(users, convertUserToV4(user, fsConfig))
-		}
-	}
-	if err := rows.Err(); err != nil {
-		return err
-	}
-
-	for _, user := range users {
-		err = sqlCommonUpdateV4CompatUser(dbHandle, user)
-		if err != nil {
-			return err
-		}
-		providerLog(logger.LevelInfo, "filesystem config downgraded for user %#v", user.Username)
-	}
-
-	ctxVersion, cancelVersion := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
-	defer cancelVersion()
-
-	return sqlCommonUpdateDatabaseVersion(ctxVersion, dbHandle, 4)
-}

+ 37 - 294
dataprovider/sqlite.go

@@ -5,6 +5,7 @@ package dataprovider
 import (
 	"context"
 	"database/sql"
+	"errors"
 	"fmt"
 	"path/filepath"
 	"strings"
@@ -19,84 +20,27 @@ import (
 )
 
 const (
-	sqliteUsersTableSQL = `CREATE TABLE "{{users}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255)
-NOT NULL UNIQUE, "password" varchar(255) NULL, "public_keys" text NULL, "home_dir" varchar(255) NOT NULL, "uid" integer NOT NULL,
-"gid" integer NOT NULL, "max_sessions" integer NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL,
-"permissions" text NOT NULL, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL,
-"last_quota_update" bigint NOT NULL, "upload_bandwidth" integer NOT NULL, "download_bandwidth" integer NOT NULL,
-"expiration_date" bigint NOT NULL, "last_login" bigint NOT NULL, "status" integer NOT NULL, "filters" text NULL,
-"filesystem" text NULL);`
-	sqliteSchemaTableSQL = `CREATE TABLE "{{schema_version}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "version" integer NOT NULL);`
-	sqliteV2SQL          = `ALTER TABLE "{{users}}" ADD COLUMN "virtual_folders" text NULL;`
-	sqliteV3SQL          = `CREATE TABLE "new__users" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255) NOT NULL UNIQUE,
-	"password" text NULL, "public_keys" text NULL, "home_dir" varchar(255) NOT NULL, "uid" integer NOT NULL,
-"gid" integer NOT NULL, "max_sessions" integer NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL,
-"permissions" text NOT NULL, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL,
-"upload_bandwidth" integer NOT NULL, "download_bandwidth" integer NOT NULL, "expiration_date" bigint NOT NULL, "last_login" bigint NOT NULL,
-"status" integer NOT NULL, "filters" text NULL, "filesystem" text NULL, "virtual_folders" text NULL);
-INSERT INTO "new__users" ("id", "username", "public_keys", "home_dir", "uid", "gid", "max_sessions", "quota_size", "quota_files",
-"permissions", "used_quota_size", "used_quota_files", "last_quota_update", "upload_bandwidth", "download_bandwidth", "expiration_date",
-"last_login", "status", "filters", "filesystem", "virtual_folders", "password") SELECT "id", "username", "public_keys", "home_dir",
-"uid", "gid", "max_sessions", "quota_size", "quota_files", "permissions", "used_quota_size", "used_quota_files", "last_quota_update",
-"upload_bandwidth", "download_bandwidth", "expiration_date", "last_login", "status", "filters", "filesystem", "virtual_folders",
-"password" FROM "{{users}}";
-DROP TABLE "{{users}}";
-ALTER TABLE "new__users" RENAME TO "{{users}}";`
-	sqliteV4SQL = `CREATE TABLE "{{folders}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "path" varchar(512) NOT NULL UNIQUE,
-"used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL);
+	sqliteInitialSQL = `CREATE TABLE "{{schema_version}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "version" integer NOT NULL);
+CREATE TABLE "{{admins}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255) NOT NULL UNIQUE,
+"password" varchar(255) NOT NULL, "email" varchar(255) NULL, "status" integer NOT NULL, "permissions" text NOT NULL,
+"filters" text NULL, "additional_info" text NULL);
+CREATE TABLE "{{folders}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(255) NOT NULL UNIQUE,
+"path" varchar(512) NULL, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL,
+"last_quota_update" bigint NOT NULL);
+CREATE TABLE "{{users}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255) NOT NULL UNIQUE,
+"password" text NULL, "public_keys" text NULL, "home_dir" varchar(512) NOT NULL, "uid" integer NOT NULL, "gid" integer NOT NULL,
+"max_sessions" integer NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL, "permissions" text NOT NULL,
+"used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL,
+"upload_bandwidth" integer NOT NULL, "download_bandwidth" integer NOT NULL, "expiration_date" bigint NOT NULL,
+"last_login" bigint NOT NULL, "status" integer NOT NULL, "filters" text NULL, "filesystem" text NULL,
+"additional_info" text NULL);
 CREATE TABLE "{{folders_mapping}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "virtual_path" varchar(512) NOT NULL,
 "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL, "folder_id" integer NOT NULL REFERENCES "{{folders}}" ("id")
 ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "user_id" integer NOT NULL REFERENCES "{{users}}" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 CONSTRAINT "unique_mapping" UNIQUE ("user_id", "folder_id"));
-CREATE TABLE "new__users" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255) NOT NULL UNIQUE, "password" text NULL,
-"public_keys" text NULL, "home_dir" varchar(512) NOT NULL, "uid" integer NOT NULL, "gid" integer NOT NULL, "max_sessions" integer NOT NULL,
-"quota_size" bigint NOT NULL, "quota_files" integer NOT NULL, "permissions" text NOT NULL, "used_quota_size" bigint NOT NULL,
-"used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL, "upload_bandwidth" integer NOT NULL, "download_bandwidth" integer NOT NULL,
-"expiration_date" bigint NOT NULL, "last_login" bigint NOT NULL, "status" integer NOT NULL, "filters" text NULL, "filesystem" text NULL);
-INSERT INTO "new__users" ("id", "username", "password", "public_keys", "home_dir", "uid", "gid", "max_sessions", "quota_size", "quota_files",
-"permissions", "used_quota_size", "used_quota_files", "last_quota_update", "upload_bandwidth", "download_bandwidth", "expiration_date",
-"last_login", "status", "filters", "filesystem") SELECT "id", "username", "password", "public_keys", "home_dir", "uid", "gid", "max_sessions",
-"quota_size", "quota_files", "permissions", "used_quota_size", "used_quota_files", "last_quota_update", "upload_bandwidth", "download_bandwidth",
-"expiration_date", "last_login", "status", "filters", "filesystem" FROM "{{users}}";
-DROP TABLE "{{users}}";
-ALTER TABLE "new__users" RENAME TO "{{users}}";
 CREATE INDEX "folders_mapping_folder_id_idx" ON "{{folders_mapping}}" ("folder_id");
 CREATE INDEX "folders_mapping_user_id_idx" ON "{{folders_mapping}}" ("user_id");
-`
-	sqliteV6SQL     = `ALTER TABLE "{{users}}" ADD COLUMN "additional_info" text NULL;`
-	sqliteV6DownSQL = `CREATE TABLE "new__users" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255) NOT NULL UNIQUE,
-"password" text NULL, "public_keys" text NULL, "home_dir" varchar(512) NOT NULL, "uid" integer NOT NULL, "gid" integer NOT NULL,
-"max_sessions" integer NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL, "permissions" text NOT NULL,
-"used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL, "upload_bandwidth" integer NOT NULL,
-"download_bandwidth" integer NOT NULL, "expiration_date" bigint NOT NULL, "last_login" bigint NOT NULL, "status" integer NOT NULL,
-"filters" text NULL, "filesystem" text NULL);
-INSERT INTO "new__users" ("id", "username", "password", "public_keys", "home_dir", "uid", "gid", "max_sessions", "quota_size", "quota_files",
-"permissions", "used_quota_size", "used_quota_files", "last_quota_update", "upload_bandwidth", "download_bandwidth", "expiration_date",
-"last_login", "status", "filters", "filesystem") SELECT "id", "username", "password", "public_keys", "home_dir", "uid", "gid", "max_sessions",
-"quota_size", "quota_files", "permissions", "used_quota_size", "used_quota_files", "last_quota_update", "upload_bandwidth", "download_bandwidth",
-"expiration_date", "last_login", "status", "filters", "filesystem" FROM "{{users}}";
-DROP TABLE "{{users}}";
-ALTER TABLE "new__users" RENAME TO "{{users}}";
-`
-	sqliteV7SQL = `CREATE TABLE "{{admins}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "username" varchar(255) NOT NULL UNIQUE,
-"password" varchar(255) NOT NULL, "email" varchar(255) NULL, "status" integer NOT NULL, "permissions" text NOT NULL, "filters" text NULL,
-"additional_info" text NULL);`
-	sqliteV7DownSQL = `DROP TABLE "{{admins}}";`
-	sqliteV8SQL     = `CREATE TABLE "new__folders" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
-"name" varchar(255) NOT NULL UNIQUE, "path" varchar(512) NULL, "used_quota_size" bigint NOT NULL,
-"used_quota_files" integer NOT NULL, "last_quota_update" bigint NOT NULL);
-INSERT INTO "new__folders" ("id", "path", "used_quota_size", "used_quota_files", "last_quota_update", "name")
-SELECT "id", "path", "used_quota_size", "used_quota_files", "last_quota_update", ('folder' || "id") FROM "{{folders}}";
-DROP TABLE "{{folders}}";
-ALTER TABLE "new__folders" RENAME TO "{{folders}}";
-`
-	sqliteV8DownSQL = `CREATE TABLE "new__folders" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
-"path" varchar(512) NOT NULL UNIQUE, "used_quota_size" bigint NOT NULL, "used_quota_files" integer NOT NULL,
-"last_quota_update" bigint NOT NULL);
-INSERT INTO "new__folders" ("id", "path", "used_quota_size", "used_quota_files", "last_quota_update")
-SELECT "id", "path", "used_quota_size", "used_quota_files", "last_quota_update" FROM "{{folders}}";
-DROP TABLE "{{folders}}";
-ALTER TABLE "new__folders" RENAME TO "{{folders}}";
+INSERT INTO {{schema_version}} (version) VALUES (8);
 `
 )
 
@@ -261,27 +205,13 @@ func (p *SQLiteProvider) initializeDatabase() error {
 	if err == nil && dbVersion.Version > 0 {
 		return ErrNoInitRequired
 	}
-	sqlUsers := strings.Replace(sqliteUsersTableSQL, "{{users}}", sqlTableUsers, 1)
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
+	initialSQL := strings.ReplaceAll(sqliteInitialSQL, "{{schema_version}}", sqlTableSchemaVersion)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{admins}}", sqlTableAdmins)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{folders}}", sqlTableFolders)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{users}}", sqlTableUsers)
+	initialSQL = strings.ReplaceAll(initialSQL, "{{folders_mapping}}", sqlTableFoldersMapping)
 
-	tx, err := p.dbHandle.BeginTx(ctx, nil)
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(sqlUsers)
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(strings.Replace(sqliteSchemaTableSQL, "{{schema_version}}", sqlTableSchemaVersion, 1))
-	if err != nil {
-		return err
-	}
-	_, err = tx.Exec(strings.Replace(initialDBVersionSQL, "{{schema_version}}", sqlTableSchemaVersion, 1))
-	if err != nil {
-		return err
-	}
-	return tx.Commit()
+	return sqlCommonExecSQLAndUpdateDBVersion(p.dbHandle, []string{initialSQL}, 8)
 }
 
 func (p *SQLiteProvider) migrateDatabase() error {
@@ -289,222 +219,35 @@ func (p *SQLiteProvider) migrateDatabase() error {
 	if err != nil {
 		return err
 	}
-	if dbVersion.Version == sqlDatabaseVersion {
-		providerLog(logger.LevelDebug, "sql database is up to date, current version: %v", dbVersion.Version)
+
+	switch version := dbVersion.Version; {
+	case version == sqlDatabaseVersion:
+		providerLog(logger.LevelDebug, "sql database is up to date, current version: %v", version)
 		return ErrNoInitRequired
-	}
-	switch dbVersion.Version {
-	case 1:
-		return updateSQLiteDatabaseFromV1(p.dbHandle)
-	case 2:
-		return updateSQLiteDatabaseFromV2(p.dbHandle)
-	case 3:
-		return updateSQLiteDatabaseFromV3(p.dbHandle)
-	case 4:
-		return updateSQLiteDatabaseFromV4(p.dbHandle)
-	case 5:
-		return updateSQLiteDatabaseFromV5(p.dbHandle)
-	case 6:
-		return updateSQLiteDatabaseFromV6(p.dbHandle)
-	case 7:
-		return updateSQLiteDatabaseFromV7(p.dbHandle)
+	case version < 8:
+		err = fmt.Errorf("database version %v is too old, please see the upgrading docs", version)
+		providerLog(logger.LevelError, "%v", err)
+		logger.ErrorToConsole("%v", err)
+		return err
 	default:
-		if dbVersion.Version > sqlDatabaseVersion {
-			providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
+		if version > sqlDatabaseVersion {
+			providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
 				sqlDatabaseVersion)
-			logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
+			logger.WarnToConsole("database version %v is newer than the supported one: %v", version,
 				sqlDatabaseVersion)
 			return nil
 		}
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
+		return fmt.Errorf("Database version not handled: %v", version)
 	}
 }
 
-//nolint:dupl
 func (p *SQLiteProvider) revertDatabase(targetVersion int) error {
 	dbVersion, err := sqlCommonGetDatabaseVersion(p.dbHandle, true)
 	if err != nil {
 		return err
 	}
 	if dbVersion.Version == targetVersion {
-		return fmt.Errorf("current version match target version, nothing to do")
+		return errors.New("current version match target version, nothing to do")
 	}
-	switch dbVersion.Version {
-	case 8:
-		err = downgradeSQLiteDatabaseFrom8To7(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradeSQLiteDatabaseFrom7To6(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradeSQLiteDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeSQLiteDatabaseFrom5To4(p.dbHandle)
-	case 7:
-		err = downgradeSQLiteDatabaseFrom7To6(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		err = downgradeSQLiteDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeSQLiteDatabaseFrom5To4(p.dbHandle)
-	case 6:
-		err = downgradeSQLiteDatabaseFrom6To5(p.dbHandle)
-		if err != nil {
-			return err
-		}
-		return downgradeSQLiteDatabaseFrom5To4(p.dbHandle)
-	case 5:
-		return downgradeSQLiteDatabaseFrom5To4(p.dbHandle)
-	default:
-		return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
-	}
-}
-
-func updateSQLiteDatabaseFromV1(dbHandle *sql.DB) error {
-	err := updateSQLiteDatabaseFrom1To2(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateSQLiteDatabaseFromV2(dbHandle)
-}
-
-func updateSQLiteDatabaseFromV2(dbHandle *sql.DB) error {
-	err := updateSQLiteDatabaseFrom2To3(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateSQLiteDatabaseFromV3(dbHandle)
-}
-
-func updateSQLiteDatabaseFromV3(dbHandle *sql.DB) error {
-	err := updateSQLiteDatabaseFrom3To4(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateSQLiteDatabaseFromV4(dbHandle)
-}
-
-func updateSQLiteDatabaseFromV4(dbHandle *sql.DB) error {
-	err := updateSQLiteDatabaseFrom4To5(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateSQLiteDatabaseFromV5(dbHandle)
-}
-
-func updateSQLiteDatabaseFromV5(dbHandle *sql.DB) error {
-	err := updateSQLiteDatabaseFrom5To6(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateSQLiteDatabaseFromV6(dbHandle)
-}
-
-func updateSQLiteDatabaseFromV6(dbHandle *sql.DB) error {
-	err := updateSQLiteDatabaseFrom6To7(dbHandle)
-	if err != nil {
-		return err
-	}
-	return updateSQLiteDatabaseFromV7(dbHandle)
-}
-
-func updateSQLiteDatabaseFromV7(dbHandle *sql.DB) error {
-	return updateSQLiteDatabaseFrom7To8(dbHandle)
-}
-
-func updateSQLiteDatabaseFrom1To2(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 1 -> 2")
-	providerLog(logger.LevelInfo, "updating database version: 1 -> 2")
-	sql := strings.Replace(sqliteV2SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 2)
-}
-
-func updateSQLiteDatabaseFrom2To3(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 2 -> 3")
-	providerLog(logger.LevelInfo, "updating database version: 2 -> 3")
-	sql := strings.ReplaceAll(sqliteV3SQL, "{{users}}", sqlTableUsers)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 3)
-}
-
-func updateSQLiteDatabaseFrom3To4(dbHandle *sql.DB) error {
-	return sqlCommonUpdateDatabaseFrom3To4(sqliteV4SQL, dbHandle)
-}
-
-func updateSQLiteDatabaseFrom4To5(dbHandle *sql.DB) error {
-	return sqlCommonUpdateDatabaseFrom4To5(dbHandle)
-}
-
-func updateSQLiteDatabaseFrom5To6(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 5 -> 6")
-	providerLog(logger.LevelInfo, "updating database version: 5 -> 6")
-	sql := strings.Replace(sqliteV6SQL, "{{users}}", sqlTableUsers, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
-}
-
-func updateSQLiteDatabaseFrom6To7(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 6 -> 7")
-	providerLog(logger.LevelInfo, "updating database version: 6 -> 7")
-	sql := strings.Replace(sqliteV7SQL, "{{admins}}", sqlTableAdmins, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 7)
-}
-
-func updateSQLiteDatabaseFrom7To8(dbHandle *sql.DB) error {
-	logger.InfoToConsole("updating database version: 7 -> 8")
-	providerLog(logger.LevelInfo, "updating database version: 7 -> 8")
-	if err := setPragmaFK(dbHandle, "OFF"); err != nil {
-		return err
-	}
-	sql := strings.ReplaceAll(sqliteV8SQL, "{{folders}}", sqlTableFolders)
-	if err := sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 8); err != nil {
-		return err
-	}
-	return setPragmaFK(dbHandle, "ON")
-}
-
-func setPragmaFK(dbHandle *sql.DB, value string) error {
-	ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
-	defer cancel()
-
-	sql := fmt.Sprintf("PRAGMA foreign_keys=%v;", value)
-
-	_, err := dbHandle.ExecContext(ctx, sql)
-	return err
-}
-
-func downgradeSQLiteDatabaseFrom8To7(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 8 -> 7")
-	providerLog(logger.LevelInfo, "downgrading database version: 8 -> 7")
-	if err := setPragmaFK(dbHandle, "OFF"); err != nil {
-		return err
-	}
-	sql := strings.ReplaceAll(sqliteV8DownSQL, "{{folders}}", sqlTableFolders)
-	if err := sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 7); err != nil {
-		return err
-	}
-	return setPragmaFK(dbHandle, "ON")
-}
-
-func downgradeSQLiteDatabaseFrom7To6(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 7 -> 6")
-	providerLog(logger.LevelInfo, "downgrading database version: 7 -> 6")
-	sql := strings.Replace(sqliteV7DownSQL, "{{admins}}", sqlTableAdmins, 1)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
-}
-
-func downgradeSQLiteDatabaseFrom6To5(dbHandle *sql.DB) error {
-	logger.InfoToConsole("downgrading database version: 6 -> 5")
-	providerLog(logger.LevelInfo, "downgrading database version: 6 -> 5")
-	sql := strings.ReplaceAll(sqliteV6DownSQL, "{{users}}", sqlTableUsers)
-	return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 5)
-}
-
-func downgradeSQLiteDatabaseFrom5To4(dbHandle *sql.DB) error {
-	return sqlCommonDowngradeDatabaseFrom5To4(dbHandle)
+	return errors.New("the current version cannot be reverted")
 }

+ 0 - 12
dataprovider/sqlqueries.go

@@ -203,15 +203,3 @@ func getDatabaseVersionQuery() string {
 func getUpdateDBVersionQuery() string {
 	return fmt.Sprintf(`UPDATE %v SET version=%v`, sqlTableSchemaVersion, sqlPlaceholders[0])
 }
-
-/*func getCompatVirtualFoldersQuery() string {
-	return fmt.Sprintf(`SELECT id,username,virtual_folders FROM %v`, sqlTableUsers)
-}*/
-
-func getCompatV4FsConfigQuery() string {
-	return fmt.Sprintf(`SELECT id,username,filesystem FROM %v`, sqlTableUsers)
-}
-
-func updateCompatV4FsConfigQuery() string {
-	return fmt.Sprintf(`UPDATE %v SET filesystem=%v WHERE id=%v`, sqlTableUsers, sqlPlaceholders[0], sqlPlaceholders[1])
-}

+ 12 - 10
go.mod

@@ -3,21 +3,22 @@ module github.com/drakkan/sftpgo
 go 1.15
 
 require (
-	cloud.google.com/go v0.76.0 // indirect
+	cloud.google.com/go v0.77.0 // indirect
 	cloud.google.com/go/storage v1.13.0
 	github.com/Azure/azure-storage-blob-go v0.13.0
 	github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
 	github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b
-	github.com/aws/aws-sdk-go v1.37.10
+	github.com/aws/aws-sdk-go v1.37.15
 	github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
 	github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d
 	github.com/fclairamb/ftpserverlib v0.12.0
 	github.com/frankban/quicktest v1.11.3 // indirect
-	github.com/go-chi/chi v1.5.2
+	github.com/go-chi/chi v1.5.3
 	github.com/go-chi/jwtauth v1.2.0
 	github.com/go-chi/render v1.0.1
 	github.com/go-ole/go-ole v1.2.5 // indirect
 	github.com/go-sql-driver/mysql v1.5.0
+	github.com/goccy/go-json v0.4.6 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 	github.com/google/uuid v1.2.0 // indirect
 	github.com/google/wire v0.5.0 // indirect
@@ -25,7 +26,7 @@ require (
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 	github.com/hashicorp/go-retryablehttp v0.6.8
 	github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
-	github.com/lestrrat-go/jwx v1.1.1
+	github.com/lestrrat-go/jwx v1.1.2
 	github.com/lib/pq v1.9.0
 	github.com/magiconair/properties v1.8.4 // indirect
 	github.com/mattn/go-sqlite3 v1.14.6
@@ -38,6 +39,7 @@ require (
 	github.com/pires/go-proxyproto v0.4.2
 	github.com/pkg/sftp v1.12.1-0.20201128220914-b5b6f3393fe9
 	github.com/prometheus/client_golang v1.9.0
+	github.com/prometheus/common v0.17.0 // indirect
 	github.com/prometheus/procfs v0.6.0 // indirect
 	github.com/rs/cors v1.7.1-0.20200626170627-8b4a00bd362b
 	github.com/rs/xid v1.2.1
@@ -59,11 +61,11 @@ require (
 	gocloud.dev/secrets/hashivault v0.22.0
 	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777
-	golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd // indirect
-	golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
-	golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
+	golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93 // indirect
+	golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43
+	golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
 	google.golang.org/api v0.40.0
-	google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d // indirect
+	google.golang.org/genproto v0.0.0-20210219173056-d891e3cb3b5b // indirect
 	gopkg.in/ini.v1 v1.62.0 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.0.0
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
@@ -72,6 +74,6 @@ require (
 replace (
 	github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
 	github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20210210202350-a2b46fc9c0d5
-	golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20201217113543-470e61ed2598
-	golang.org/x/net => github.com/drakkan/net v0.0.0-20210201075003-5fb2b186574d
+	golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20210221212101-dc57d1956176
+	golang.org/x/net => github.com/drakkan/net v0.0.0-20210221212420-9117fa75ae3d
 )

+ 25 - 23
go.sum

@@ -19,8 +19,8 @@ cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0K
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
 cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
-cloud.google.com/go v0.76.0 h1:Ckw+E/QYZgd/5bpI4wz4h6f+jmpvh9S9uSrKNnbicJI=
-cloud.google.com/go v0.76.0/go.mod h1:r9EvIAvLrunusnetGdQ50M/gKui1x3zdGW/VELGkdpw=
+cloud.google.com/go v0.77.0 h1:qA5V5+uQf6Mgr+tmFI8UT3D/ELyhIYkPwNGao/3Y+sQ=
+cloud.google.com/go v0.77.0/go.mod h1:R8fYSLIilC247Iu8WS2OGHw1E/Ufn7Pd7HiDjTqiURs=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -116,8 +116,8 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo
 github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.36.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
-github.com/aws/aws-sdk-go v1.37.10 h1:LRwl+97B4D69Z7tz+eRUxJ1C7baBaIYhgrn5eLtua+Q=
-github.com/aws/aws-sdk-go v1.37.10/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
+github.com/aws/aws-sdk-go v1.37.15 h1:W7l7gLLMcYRlg6a+uvf3Zz4jYwdqYzhe5ymqwWoOhp4=
+github.com/aws/aws-sdk-go v1.37.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -167,12 +167,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
-github.com/drakkan/crypto v0.0.0-20201217113543-470e61ed2598 h1:JawLnfGaG2uL0eFTRRX0t8J7pp0FhojAt9FSPqzV38U=
-github.com/drakkan/crypto v0.0.0-20201217113543-470e61ed2598/go.mod h1:HCh3rfXxsHzqOEbzc/nqz6WnUhb7Nv19n/o64V0Zmbg=
+github.com/drakkan/crypto v0.0.0-20210221212101-dc57d1956176 h1:ZQY12NIZ1HtS8Jqrw0oOEEgYfh3JtPLYsiZNDdkdW0w=
+github.com/drakkan/crypto v0.0.0-20210221212101-dc57d1956176/go.mod h1:HCh3rfXxsHzqOEbzc/nqz6WnUhb7Nv19n/o64V0Zmbg=
 github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
 github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
-github.com/drakkan/net v0.0.0-20210201075003-5fb2b186574d h1:h2rU/lTUkEYB3y4k6+FgQNMajf4uE+sbMRn85kT+VTQ=
-github.com/drakkan/net v0.0.0-20210201075003-5fb2b186574d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+github.com/drakkan/net v0.0.0-20210221212420-9117fa75ae3d h1:VUM47Q+HId0s2Kr6Cx/Iej3V3JQHxw/C9FzMRlaicDc=
+github.com/drakkan/net v0.0.0-20210221212420-9117fa75ae3d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 github.com/drakkan/sftp v0.0.0-20210210202350-a2b46fc9c0d5 h1:jVxjoPrGY9Ypw65tTHRdDvumOE3ys2fLZfvFT6+gFPU=
 github.com/drakkan/sftp v0.0.0-20210210202350-a2b46fc9c0d5/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -208,10 +208,9 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
-github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
 github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
-github.com/go-chi/chi v1.5.2 h1:YcLIBANL4OTaAOcTdp//sskGa0yGACQMCtbnr7YEn0Q=
-github.com/go-chi/chi v1.5.2/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
+github.com/go-chi/chi v1.5.3 h1:+DVDS9/D3MTbEu3WrrH3oz9oP6PlSPSNj8LLw3X17yU=
+github.com/go-chi/chi v1.5.3/go.mod h1:Q8xfe6s3fjZyMr8ZTv5jL+vxhVaFyCq2s+RvSfzTD0E=
 github.com/go-chi/jwtauth v1.2.0 h1:Z116SPpevIABBYsv8ih/AHYBHmd4EufKSKsLUnWdrTM=
 github.com/go-chi/jwtauth v1.2.0/go.mod h1:NTUpKoTQV6o25UwYE6w/VaLUu83hzrVKYTVo+lE6qDA=
 github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
@@ -247,8 +246,10 @@ github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr6
 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
 github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
 github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
-github.com/goccy/go-json v0.3.5 h1:HqrLjEWx7hD62JRhBh+mHv+rEEzBANIu6O0kbDlaLzU=
 github.com/goccy/go-json v0.3.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.4.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.4.6 h1:zpWhZVrIu+WfuJ1waElFa1KOnKj2p0H9wpJn/TTY0zQ=
+github.com/goccy/go-json v0.4.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -458,8 +459,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
 github.com/lestrrat-go/iter v1.0.0 h1:QD+hHQPDSHC4rCJkZYY/yXChYr/vjfBopKekTc+7l4Q=
 github.com/lestrrat-go/iter v1.0.0/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
 github.com/lestrrat-go/jwx v1.1.0/go.mod h1:vn9FzD6gJtKkgYs7RTKV7CjWtEka8F/voUollhnn4QE=
-github.com/lestrrat-go/jwx v1.1.1 h1:L7TqffHhO0qSyUcDGfCkDV42GQMp9fNOBi/zFOigMEY=
-github.com/lestrrat-go/jwx v1.1.1/go.mod h1:vn9FzD6gJtKkgYs7RTKV7CjWtEka8F/voUollhnn4QE=
+github.com/lestrrat-go/jwx v1.1.2 h1:Cpl6Y8KrMEilSdaX0YW9Zrdy2hPQ7lzYOh7bRAYLGNU=
+github.com/lestrrat-go/jwx v1.1.2/go.mod h1:gr+Z6vxDJQoGW6fA3b5PMgPE8AmaRXNUImLKp/S3vrk=
 github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
 github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
@@ -592,8 +593,9 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
 github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
+github.com/prometheus/common v0.17.0 h1:kDIZLI74SS+3tedSvEkykgBkD7txMxaJAPj8DtJUKYA=
+github.com/prometheus/common v0.17.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -661,7 +663,6 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3
 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -762,8 +763,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd h1:2arJsLyTCJGek+eeptQ3z49Rqndm0f+zvvpwNIXWNIA=
-golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93 h1:alLDrZkL34Y2bnGHfvC1CYBRBXCXgx8AC2vY4MRtYX4=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -831,8 +832,9 @@ golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 h1:SgQ6LNaYJU0JIuEHv9+s6EbhSCwYeAf5Yvj6lpYlqAE=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -850,8 +852,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
-golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1000,10 +1002,10 @@ google.golang.org/genproto v0.0.0-20201203001206-6486ece9c497/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210203152818-3206188e46ba/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d h1:Edhcm0CKDPLQIecHCp5Iz57Lo7MfT6zUFBAlocmOjcY=
 google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210219173056-d891e3cb3b5b h1:zTeTu5p/EXQSqNJboHUw32wdNFYQTT9vSc+ibSpCoLk=
+google.golang.org/genproto v0.0.0-20210219173056-d891e3cb3b5b/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=

+ 1 - 1
version/version.go

@@ -2,7 +2,7 @@ package version
 
 import "strings"
 
-const version = "2.0.2-dev"
+const version = "2.0.90-dev"
 
 var (
 	commit = ""