mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-08 07:10:56 +03:00
21
README.md
21
README.md
@@ -119,6 +119,27 @@ sftpgo initprovider --help
|
|||||||
|
|
||||||
You can disable automatic data provider checks/updates at startup by setting the `update_mode` configuration key to `1`.
|
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.
|
||||||
|
|
||||||
|
We support the follwing schema versions:
|
||||||
|
|
||||||
|
- `6`, this is the current git master
|
||||||
|
- `4`, this is the schema for v1.0.0-v1.2.x
|
||||||
|
|
||||||
|
So, if you plan to downgrade from git master to 1.2.x, 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sftpgo revertprovider --help
|
||||||
|
```
|
||||||
|
|
||||||
|
The `revertprovider` command is not supported for the memory provider.
|
||||||
|
|
||||||
## Users and folders management
|
## Users and folders management
|
||||||
|
|
||||||
After starting SFTPGo you can manage users and folders using:
|
After starting SFTPGo you can manage users and folders using:
|
||||||
|
|||||||
53
cmd/revertprovider.go
Normal file
53
cmd/revertprovider.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/drakkan/sftpgo/config"
|
||||||
|
"github.com/drakkan/sftpgo/dataprovider"
|
||||||
|
"github.com/drakkan/sftpgo/logger"
|
||||||
|
"github.com/drakkan/sftpgo/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
revertProviderTargetVersion int
|
||||||
|
revertProviderCmd = &cobra.Command{
|
||||||
|
Use: "revertprovider",
|
||||||
|
Short: "Revert the configured data provider to a previous version",
|
||||||
|
Long: `This command reads the data provider connection details from the specified
|
||||||
|
configuration file and restore the provider schema and/or data to a previous version.
|
||||||
|
This command is not supported for the memory provider.
|
||||||
|
|
||||||
|
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)
|
||||||
|
configDir = utils.CleanDirInput(configDir)
|
||||||
|
err := config.LoadConfig(configDir, configFile)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("Unable to initialize data provider, config load error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
providerConf := config.GetProviderConf()
|
||||||
|
logger.InfoToConsole("Reverting provider: %#v config file: %#v target version %v", providerConf.Driver,
|
||||||
|
viper.ConfigFileUsed(), revertProviderTargetVersion)
|
||||||
|
err = dataprovider.RevertDatabase(providerConf, configDir, revertProviderTargetVersion)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("Error reverting provider: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
logger.InfoToConsole("Data provider successfully reverted")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
addConfigFlags(revertProviderCmd)
|
||||||
|
revertProviderCmd.Flags().IntVar(&revertProviderTargetVersion, "to-version", 0, `4 means the version supported in v1.0.0-v1.2.x`)
|
||||||
|
|
||||||
|
rootCmd.AddCommand(revertProviderCmd)
|
||||||
|
}
|
||||||
@@ -706,6 +706,29 @@ func (p BoltProvider) migrateDatabase() error {
|
|||||||
return updateBoltDatabaseFromV3(p.dbHandle)
|
return updateBoltDatabaseFromV3(p.dbHandle)
|
||||||
case 4:
|
case 4:
|
||||||
return updateBoltDatabaseFromV4(p.dbHandle)
|
return updateBoltDatabaseFromV4(p.dbHandle)
|
||||||
|
default:
|
||||||
|
if dbVersion.Version > sqlDatabaseVersion {
|
||||||
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
boltDatabaseVersion)
|
||||||
|
logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
boltDatabaseVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p BoltProvider) revertDatabase(targetVersion int) error {
|
||||||
|
dbVersion, err := getBoltDatabaseVersion(p.dbHandle)
|
||||||
|
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)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
@@ -846,6 +869,23 @@ func removeUserFromFolderMapping(folder vfs.VirtualFolder, user User, bucket *bo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateV4BoltCompatUser(dbHandle *bolt.DB, user compatUserV4) error {
|
||||||
|
return dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket, _, err := getBuckets(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 {
|
func updateV4BoltUser(dbHandle *bolt.DB, user User) error {
|
||||||
err := validateUser(&user)
|
err := validateUser(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1027,6 +1067,48 @@ func updateDatabaseFrom3To4(dbHandle *bolt.DB) error {
|
|||||||
return err
|
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 := getBuckets(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 {
|
func updateDatabaseFrom4To5(dbHandle *bolt.DB) error {
|
||||||
logger.InfoToConsole("updating bolt database version: 4 -> 5")
|
logger.InfoToConsole("updating bolt database version: 4 -> 5")
|
||||||
providerLog(logger.LevelInfo, "updating bolt database version: 4 -> 5")
|
providerLog(logger.LevelInfo, "updating bolt database version: 4 -> 5")
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package dataprovider
|
package dataprovider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/logger"
|
"github.com/drakkan/sftpgo/logger"
|
||||||
|
"github.com/drakkan/sftpgo/utils"
|
||||||
"github.com/drakkan/sftpgo/vfs"
|
"github.com/drakkan/sftpgo/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -130,6 +132,34 @@ func createUserFromV4(u compatUserV4, fsConfig Filesystem) User {
|
|||||||
return user
|
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) (vfs.Secret, error) {
|
func getCGSCredentialsFromV4(config compatGCSFsConfigV4) (vfs.Secret, error) {
|
||||||
var secret vfs.Secret
|
var secret vfs.Secret
|
||||||
var err error
|
var err error
|
||||||
@@ -150,6 +180,104 @@ func getCGSCredentialsFromV4(config compatGCSFsConfigV4) (vfs.Secret, error) {
|
|||||||
return secret, err
|
return secret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCGSCredentialsFromV6(config vfs.GCSFsConfig, username string) (string, error) {
|
||||||
|
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.Payload, 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.Payload)
|
||||||
|
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.Payload)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fsV4, nil
|
||||||
|
}
|
||||||
|
|
||||||
func convertFsConfigFromV4(compatFs compatFilesystemV4, username string) (Filesystem, error) {
|
func convertFsConfigFromV4(compatFs compatFilesystemV4, username string) (Filesystem, error) {
|
||||||
fsConfig := Filesystem{
|
fsConfig := Filesystem{
|
||||||
Provider: compatFs.Provider,
|
Provider: compatFs.Provider,
|
||||||
|
|||||||
@@ -377,6 +377,7 @@ type Provider interface {
|
|||||||
reloadConfig() error
|
reloadConfig() error
|
||||||
initializeDatabase() error
|
initializeDatabase() error
|
||||||
migrateDatabase() error
|
migrateDatabase() error
|
||||||
|
revertDatabase(targetVersion int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the data provider.
|
// Initialize the data provider.
|
||||||
@@ -477,6 +478,12 @@ func validateSQLTablesPrefix() error {
|
|||||||
func InitializeDatabase(cnf Config, basePath string) error {
|
func InitializeDatabase(cnf Config, basePath string) error {
|
||||||
config = cnf
|
config = cnf
|
||||||
|
|
||||||
|
if filepath.IsAbs(config.CredentialsPath) {
|
||||||
|
credentialsDirPath = config.CredentialsPath
|
||||||
|
} else {
|
||||||
|
credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
|
||||||
|
}
|
||||||
|
|
||||||
err := createProvider(basePath)
|
err := createProvider(basePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -488,6 +495,27 @@ func InitializeDatabase(cnf Config, basePath string) error {
|
|||||||
return provider.migrateDatabase()
|
return provider.migrateDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RevertDatabase restores schema and/or data to a previous version
|
||||||
|
func RevertDatabase(cnf Config, basePath string, targetVersion int) error {
|
||||||
|
config = cnf
|
||||||
|
|
||||||
|
if filepath.IsAbs(config.CredentialsPath) {
|
||||||
|
credentialsDirPath = config.CredentialsPath
|
||||||
|
} else {
|
||||||
|
credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := createProvider(basePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = provider.initializeDatabase()
|
||||||
|
if err != nil && err != ErrNoInitRequired {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return provider.revertDatabase(targetVersion)
|
||||||
|
}
|
||||||
|
|
||||||
// CheckUserAndPass retrieves the SFTP user with the given username and password if a match is found or an error
|
// CheckUserAndPass retrieves the SFTP user with the given username and password if a match is found or an error
|
||||||
func CheckUserAndPass(username, password, ip, protocol string) (User, error) {
|
func CheckUserAndPass(username, password, ip, protocol string) (User, error) {
|
||||||
if len(config.ExternalAuthHook) > 0 && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
|
if len(config.ExternalAuthHook) > 0 && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
|
||||||
|
|||||||
@@ -677,3 +677,7 @@ func (p MemoryProvider) initializeDatabase() error {
|
|||||||
func (p MemoryProvider) migrateDatabase() error {
|
func (p MemoryProvider) migrateDatabase() error {
|
||||||
return ErrNoInitRequired
|
return ErrNoInitRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p MemoryProvider) revertDatabase(targetVersion int) error {
|
||||||
|
return errors.New("memory provider does not store data, revert not possible")
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ const (
|
|||||||
"ALTER TABLE `{{folders_mapping}}` ADD CONSTRAINT `unique_mapping` UNIQUE (`user_id`, `folder_id`);" +
|
"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_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;"
|
"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;"
|
mysqlV6SQL = "ALTER TABLE `{{users}}` ADD COLUMN `additional_info` longtext NULL;"
|
||||||
|
mysqlV6DownSQL = "ALTER TABLE `{{users}}` DROP COLUMN `additional_info`;"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MySQLProvider auth provider for MySQL/MariaDB database
|
// MySQLProvider auth provider for MySQL/MariaDB database
|
||||||
@@ -220,6 +221,35 @@ func (p MySQLProvider) migrateDatabase() error {
|
|||||||
return updateMySQLDatabaseFromV4(p.dbHandle)
|
return updateMySQLDatabaseFromV4(p.dbHandle)
|
||||||
case 5:
|
case 5:
|
||||||
return updateMySQLDatabaseFromV5(p.dbHandle)
|
return updateMySQLDatabaseFromV5(p.dbHandle)
|
||||||
|
default:
|
||||||
|
if dbVersion.Version > sqlDatabaseVersion {
|
||||||
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
sqlDatabaseVersion)
|
||||||
|
logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
sqlDatabaseVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 6:
|
||||||
|
err = downgradeMySQLDatabaseFrom6To5(p.dbHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return downgradeMySQLDatabaseFrom5To4(p.dbHandle)
|
||||||
|
case 5:
|
||||||
|
return downgradeMySQLDatabaseFrom5To4(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
@@ -289,3 +319,14 @@ func updateMySQLDatabaseFrom5To6(dbHandle *sql.DB) error {
|
|||||||
sql := strings.Replace(mysqlV6SQL, "{{users}}", sqlTableUsers, 1)
|
sql := strings.Replace(mysqlV6SQL, "{{users}}", sqlTableUsers, 1)
|
||||||
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ ALTER TABLE "{{folders_mapping}}" ADD CONSTRAINT "folders_mapping_user_id_fk_use
|
|||||||
CREATE INDEX "folders_mapping_folder_id_idx" ON "{{folders_mapping}}" ("folder_id");
|
CREATE INDEX "folders_mapping_folder_id_idx" ON "{{folders_mapping}}" ("folder_id");
|
||||||
CREATE INDEX "folders_mapping_user_id_idx" ON "{{folders_mapping}}" ("user_id");
|
CREATE INDEX "folders_mapping_user_id_idx" ON "{{folders_mapping}}" ("user_id");
|
||||||
`
|
`
|
||||||
pgsqlV6SQL = `ALTER TABLE "{{users}}" ADD COLUMN "additional_info" text NULL;`
|
pgsqlV6SQL = `ALTER TABLE "{{users}}" ADD COLUMN "additional_info" text NULL;`
|
||||||
|
pgsqlV6DownSQL = `ALTER TABLE "{{users}}" DROP COLUMN "additional_info" CASCADE;`
|
||||||
)
|
)
|
||||||
|
|
||||||
// PGSQLProvider auth provider for PostgreSQL database
|
// PGSQLProvider auth provider for PostgreSQL database
|
||||||
@@ -219,6 +220,35 @@ func (p PGSQLProvider) migrateDatabase() error {
|
|||||||
return updatePGSQLDatabaseFromV4(p.dbHandle)
|
return updatePGSQLDatabaseFromV4(p.dbHandle)
|
||||||
case 5:
|
case 5:
|
||||||
return updatePGSQLDatabaseFromV5(p.dbHandle)
|
return updatePGSQLDatabaseFromV5(p.dbHandle)
|
||||||
|
default:
|
||||||
|
if dbVersion.Version > sqlDatabaseVersion {
|
||||||
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
sqlDatabaseVersion)
|
||||||
|
logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
sqlDatabaseVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
switch dbVersion.Version {
|
||||||
|
case 6:
|
||||||
|
err = downgradePGSQLDatabaseFrom6To5(p.dbHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return downgradePGSQLDatabaseFrom5To4(p.dbHandle)
|
||||||
|
case 5:
|
||||||
|
return downgradePGSQLDatabaseFrom5To4(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
@@ -288,3 +318,14 @@ func updatePGSQLDatabaseFrom5To6(dbHandle *sql.DB) error {
|
|||||||
sql := strings.Replace(pgsqlV6SQL, "{{users}}", sqlTableUsers, 1)
|
sql := strings.Replace(pgsqlV6SQL, "{{users}}", sqlTableUsers, 1)
|
||||||
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -948,6 +948,7 @@ func sqlCommonUpdateDatabaseFrom3To4(sqlV4 string, dbHandle *sql.DB) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:dupl
|
||||||
func sqlCommonUpdateDatabaseFrom4To5(dbHandle *sql.DB) error {
|
func sqlCommonUpdateDatabaseFrom4To5(dbHandle *sql.DB) error {
|
||||||
logger.InfoToConsole("updating database version: 4 -> 5")
|
logger.InfoToConsole("updating database version: 4 -> 5")
|
||||||
providerLog(logger.LevelInfo, "updating database version: 4 -> 5")
|
providerLog(logger.LevelInfo, "updating database version: 4 -> 5")
|
||||||
@@ -1005,6 +1006,26 @@ func sqlCommonUpdateDatabaseFrom4To5(dbHandle *sql.DB) error {
|
|||||||
return sqlCommonUpdateDatabaseVersion(ctxVersion, dbHandle, 5)
|
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 {
|
func sqlCommonUpdateV4User(dbHandle *sql.DB, user User) error {
|
||||||
err := validateFilesystemConfig(&user)
|
err := validateFilesystemConfig(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1032,3 +1053,61 @@ func sqlCommonUpdateV4User(dbHandle *sql.DB, user User) error {
|
|||||||
_, err = stmt.ExecContext(ctx, string(fsConfig), user.ID)
|
_, err = stmt.ExecContext(ctx, string(fsConfig), user.ID)
|
||||||
return err
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,7 +63,21 @@ ALTER TABLE "new__users" RENAME TO "{{users}}";
|
|||||||
CREATE INDEX "folders_mapping_folder_id_idx" ON "{{folders_mapping}}" ("folder_id");
|
CREATE INDEX "folders_mapping_folder_id_idx" ON "{{folders_mapping}}" ("folder_id");
|
||||||
CREATE INDEX "folders_mapping_user_id_idx" ON "{{folders_mapping}}" ("user_id");
|
CREATE INDEX "folders_mapping_user_id_idx" ON "{{folders_mapping}}" ("user_id");
|
||||||
`
|
`
|
||||||
sqliteV6SQL = `ALTER TABLE "{{users}}" ADD COLUMN "additional_info" text NULL;`
|
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}}";
|
||||||
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
// SQLiteProvider auth provider for SQLite database
|
// SQLiteProvider auth provider for SQLite database
|
||||||
@@ -242,6 +256,35 @@ func (p SQLiteProvider) migrateDatabase() error {
|
|||||||
return updateSQLiteDatabaseFromV4(p.dbHandle)
|
return updateSQLiteDatabaseFromV4(p.dbHandle)
|
||||||
case 5:
|
case 5:
|
||||||
return updateSQLiteDatabaseFromV5(p.dbHandle)
|
return updateSQLiteDatabaseFromV5(p.dbHandle)
|
||||||
|
default:
|
||||||
|
if dbVersion.Version > sqlDatabaseVersion {
|
||||||
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
sqlDatabaseVersion)
|
||||||
|
logger.WarnToConsole("database version %v is newer than the supported: %v", dbVersion.Version,
|
||||||
|
sqlDatabaseVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
switch dbVersion.Version {
|
||||||
|
case 6:
|
||||||
|
err = downgradeSQLiteDatabaseFrom6To5(p.dbHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return downgradeSQLiteDatabaseFrom5To4(p.dbHandle)
|
||||||
|
case 5:
|
||||||
|
return downgradeSQLiteDatabaseFrom5To4(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("Database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
@@ -311,3 +354,14 @@ func updateSQLiteDatabaseFrom5To6(dbHandle *sql.DB) error {
|
|||||||
sql := strings.Replace(sqliteV6SQL, "{{users}}", sqlTableUsers, 1)
|
sql := strings.Replace(sqliteV6SQL, "{{users}}", sqlTableUsers, 1)
|
||||||
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 6)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user