replace utils.Contains with slices.Contains

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-07-24 18:27:13 +02:00
parent bd5eb03d9c
commit d94f80c8da
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
51 changed files with 353 additions and 322 deletions

View file

@ -30,6 +30,7 @@ import (
"net/url"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
@ -249,7 +250,7 @@ func (c *Configuration) Initialize(configDir string) error {
if c.RenewDays < 1 {
return fmt.Errorf("invalid number of days remaining before renewal: %d", c.RenewDays)
}
if !util.Contains(supportedKeyTypes, c.KeyType) {
if !slices.Contains(supportedKeyTypes, c.KeyType) {
return fmt.Errorf("invalid key type %q", c.KeyType)
}
caURL, err := url.Parse(c.CAEndpoint)

View file

@ -17,10 +17,9 @@ package command
import (
"fmt"
"slices"
"strings"
"time"
"github.com/drakkan/sftpgo/v2/internal/util"
)
const (
@ -117,7 +116,7 @@ func (c Config) Initialize() error {
}
// don't validate args, we allow to pass empty arguments
if cmd.Hook != "" {
if !util.Contains(supportedHooks, cmd.Hook) {
if !slices.Contains(supportedHooks, cmd.Hook) {
return fmt.Errorf("invalid hook name %q, supported values: %+v", cmd.Hook, supportedHooks)
}
}

View file

@ -25,6 +25,7 @@ import (
"os/exec"
"path"
"path/filepath"
"slices"
"strings"
"sync/atomic"
"time"
@ -86,7 +87,7 @@ func InitializeActionHandler(handler ActionHandler) {
func ExecutePreAction(conn *BaseConnection, operation, filePath, virtualPath string, fileSize int64, openFlags int) (int, error) {
var event *notifier.FsEvent
hasNotifiersPlugin := plugin.Handler.HasNotifiers()
hasHook := util.Contains(Config.Actions.ExecuteOn, operation)
hasHook := slices.Contains(Config.Actions.ExecuteOn, operation)
hasRules := eventManager.hasFsRules()
if !hasHook && !hasNotifiersPlugin && !hasRules {
return 0, nil
@ -132,7 +133,7 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
fileSize int64, err error, elapsed int64, metadata map[string]string,
) error {
hasNotifiersPlugin := plugin.Handler.HasNotifiers()
hasHook := util.Contains(Config.Actions.ExecuteOn, operation)
hasHook := slices.Contains(Config.Actions.ExecuteOn, operation)
hasRules := eventManager.hasFsRules()
if !hasHook && !hasNotifiersPlugin && !hasRules {
return nil
@ -173,7 +174,7 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
}
}
if hasHook {
if util.Contains(Config.Actions.ExecuteSync, operation) {
if slices.Contains(Config.Actions.ExecuteSync, operation) {
_, err := actionHandler.Handle(notification)
return err
}
@ -247,7 +248,7 @@ func newActionNotification(
type defaultActionHandler struct{}
func (h *defaultActionHandler) Handle(event *notifier.FsEvent) (int, error) {
if !util.Contains(Config.Actions.ExecuteOn, event.Action) {
if !slices.Contains(Config.Actions.ExecuteOn, event.Action) {
return 0, nil
}

View file

@ -25,6 +25,7 @@ import (
"os"
"os/exec"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
@ -207,7 +208,7 @@ func Initialize(c Configuration, isShared int) error {
Config.rateLimitersList = rateLimitersList
}
if c.DefenderConfig.Enabled {
if !util.Contains(supportedDefenderDrivers, c.DefenderConfig.Driver) {
if !slices.Contains(supportedDefenderDrivers, c.DefenderConfig.Driver) {
return fmt.Errorf("unsupported defender driver %q", c.DefenderConfig.Driver)
}
var defender Defender
@ -777,7 +778,7 @@ func (c *Configuration) checkPostDisconnectHook(remoteAddr, protocol, username,
if c.PostDisconnectHook == "" {
return
}
if !util.Contains(disconnHookProtocols, protocol) {
if !slices.Contains(disconnHookProtocols, protocol) {
return
}
go c.executePostDisconnectHook(remoteAddr, protocol, username, connID, connectionTime)
@ -1019,7 +1020,7 @@ func (conns *ActiveConnections) Remove(connectionID string) {
metric.UpdateActiveConnectionsSize(lastIdx)
logger.Debug(conn.GetProtocol(), conn.GetID(), "connection removed, local address %q, remote address %q close fs error: %v, num open connections: %d",
conn.GetLocalAddress(), conn.GetRemoteAddress(), err, lastIdx)
if conn.GetProtocol() == ProtocolFTP && conn.GetUsername() == "" && !util.Contains(ftpLoginCommands, conn.GetCommand()) {
if conn.GetProtocol() == ProtocolFTP && conn.GetUsername() == "" && !slices.Contains(ftpLoginCommands, conn.GetCommand()) {
ip := util.GetIPFromRemoteAddress(conn.GetRemoteAddress())
logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTried, ProtocolFTP,
dataprovider.ErrNoAuthTried.Error())

View file

@ -23,6 +23,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"slices"
"sync"
"testing"
"time"
@ -1226,8 +1227,8 @@ func TestFolderCopy(t *testing.T) {
folder.ID = 2
folder.Users = []string{"user3"}
require.Len(t, folderCopy.Users, 2)
require.True(t, util.Contains(folderCopy.Users, "user1"))
require.True(t, util.Contains(folderCopy.Users, "user2"))
require.True(t, slices.Contains(folderCopy.Users, "user1"))
require.True(t, slices.Contains(folderCopy.Users, "user2"))
require.Equal(t, int64(1), folderCopy.ID)
require.Equal(t, folder.Name, folderCopy.Name)
require.Equal(t, folder.MappedPath, folderCopy.MappedPath)
@ -1243,7 +1244,7 @@ func TestFolderCopy(t *testing.T) {
folderCopy = folder.GetACopy()
folder.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
require.Len(t, folderCopy.Users, 1)
require.True(t, util.Contains(folderCopy.Users, "user3"))
require.True(t, slices.Contains(folderCopy.Users, "user3"))
require.Equal(t, int64(2), folderCopy.ID)
require.Equal(t, folder.Name, folderCopy.Name)
require.Equal(t, folder.MappedPath, folderCopy.MappedPath)

View file

@ -63,7 +63,7 @@ type BaseConnection struct {
// NewBaseConnection returns a new BaseConnection
func NewBaseConnection(id, protocol, localAddr, remoteAddr string, user dataprovider.User) *BaseConnection {
connID := id
if util.Contains(supportedProtocols, protocol) {
if slices.Contains(supportedProtocols, protocol) {
connID = fmt.Sprintf("%s_%s", protocol, id)
}
user.UploadBandwidth, user.DownloadBandwidth = user.GetBandwidthForIP(util.GetIPFromRemoteAddress(remoteAddr), connID)
@ -132,7 +132,7 @@ func (c *BaseConnection) GetRemoteIP() string {
// SetProtocol sets the protocol for this connection
func (c *BaseConnection) SetProtocol(protocol string) {
c.protocol = protocol
if util.Contains(supportedProtocols, c.protocol) {
if slices.Contains(supportedProtocols, c.protocol) {
c.ID = fmt.Sprintf("%v_%v", c.protocol, c.ID)
}
}

View file

@ -22,6 +22,7 @@ import (
"path"
"path/filepath"
"runtime"
"slices"
"strconv"
"testing"
"time"
@ -389,7 +390,7 @@ func TestErrorsMapping(t *testing.T) {
err := conn.GetFsError(fs, os.ErrNotExist)
if protocol == ProtocolSFTP {
assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
} else if util.Contains(osErrorsProtocols, protocol) {
} else if slices.Contains(osErrorsProtocols, protocol) {
assert.EqualError(t, err, os.ErrNotExist.Error())
} else {
assert.EqualError(t, err, ErrNotExist.Error())

View file

@ -31,6 +31,7 @@ import (
"os/exec"
"path"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
@ -307,7 +308,7 @@ func (*eventRulesContainer) checkIPDLoginEventMatch(conditions *dataprovider.Eve
}
func (*eventRulesContainer) checkProviderEventMatch(conditions *dataprovider.EventConditions, params *EventParams) bool {
if !util.Contains(conditions.ProviderEvents, params.Event) {
if !slices.Contains(conditions.ProviderEvents, params.Event) {
return false
}
if !checkEventConditionPatterns(params.Name, conditions.Options.Names) {
@ -316,14 +317,14 @@ func (*eventRulesContainer) checkProviderEventMatch(conditions *dataprovider.Eve
if !checkEventConditionPatterns(params.Role, conditions.Options.RoleNames) {
return false
}
if len(conditions.Options.ProviderObjects) > 0 && !util.Contains(conditions.Options.ProviderObjects, params.ObjectType) {
if len(conditions.Options.ProviderObjects) > 0 && !slices.Contains(conditions.Options.ProviderObjects, params.ObjectType) {
return false
}
return true
}
func (*eventRulesContainer) checkFsEventMatch(conditions *dataprovider.EventConditions, params *EventParams) bool {
if !util.Contains(conditions.FsEvents, params.Event) {
if !slices.Contains(conditions.FsEvents, params.Event) {
return false
}
if !checkEventConditionPatterns(params.Name, conditions.Options.Names) {
@ -338,7 +339,7 @@ func (*eventRulesContainer) checkFsEventMatch(conditions *dataprovider.EventCond
if !checkEventConditionPatterns(params.VirtualPath, conditions.Options.FsPaths) {
return false
}
if len(conditions.Options.Protocols) > 0 && !util.Contains(conditions.Options.Protocols, params.Protocol) {
if len(conditions.Options.Protocols) > 0 && !slices.Contains(conditions.Options.Protocols, params.Protocol) {
return false
}
if params.Event == operationUpload || params.Event == operationDownload {

View file

@ -30,6 +30,7 @@ import (
"path"
"path/filepath"
"runtime"
"slices"
"strings"
"sync"
"testing"
@ -3978,9 +3979,9 @@ func TestEventRule(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 3)
assert.True(t, util.Contains(email.To, "test1@example.com"))
assert.True(t, util.Contains(email.To, "test2@example.com"))
assert.True(t, util.Contains(email.To, "test3@example.com"))
assert.True(t, slices.Contains(email.To, "test1@example.com"))
assert.True(t, slices.Contains(email.To, "test2@example.com"))
assert.True(t, slices.Contains(email.To, "test3@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "upload" from "%s" status OK`, user.Username))
// test the failure action, we download a file that exceeds the transfer quota limit
err = writeSFTPFileNoCheck(path.Join("subdir1", testFileName), 1*1024*1024+65535, client)
@ -3999,9 +4000,9 @@ func TestEventRule(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 3)
assert.True(t, util.Contains(email.To, "test1@example.com"))
assert.True(t, util.Contains(email.To, "test2@example.com"))
assert.True(t, util.Contains(email.To, "test3@example.com"))
assert.True(t, slices.Contains(email.To, "test1@example.com"))
assert.True(t, slices.Contains(email.To, "test2@example.com"))
assert.True(t, slices.Contains(email.To, "test3@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "download" from "%s" status KO`, user.Username))
assert.Contains(t, email.Data, `"download" failed`)
assert.Contains(t, email.Data, common.ErrReadQuotaExceeded.Error())
@ -4019,7 +4020,7 @@ func TestEventRule(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "failure@example.com"))
assert.True(t, slices.Contains(email.To, "failure@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: Failed "upload" from "%s"`, user.Username))
assert.Contains(t, email.Data, fmt.Sprintf(`action %q failed`, action1.Name))
// now test the download rule
@ -4036,9 +4037,9 @@ func TestEventRule(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 3)
assert.True(t, util.Contains(email.To, "test1@example.com"))
assert.True(t, util.Contains(email.To, "test2@example.com"))
assert.True(t, util.Contains(email.To, "test3@example.com"))
assert.True(t, slices.Contains(email.To, "test1@example.com"))
assert.True(t, slices.Contains(email.To, "test2@example.com"))
assert.True(t, slices.Contains(email.To, "test3@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "download" from "%s"`, user.Username))
}
// test upload action command with arguments
@ -4079,9 +4080,9 @@ func TestEventRule(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 3)
assert.True(t, util.Contains(email.To, "test1@example.com"))
assert.True(t, util.Contains(email.To, "test2@example.com"))
assert.True(t, util.Contains(email.To, "test3@example.com"))
assert.True(t, slices.Contains(email.To, "test1@example.com"))
assert.True(t, slices.Contains(email.To, "test2@example.com"))
assert.True(t, slices.Contains(email.To, "test3@example.com"))
assert.Contains(t, email.Data, `Subject: New "delete" from "admin"`)
_, err = httpdtest.RemoveEventRule(rule3, http.StatusOK)
assert.NoError(t, err)
@ -4236,7 +4237,7 @@ func TestEventRuleProviderEvents(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test3@example.com"))
assert.True(t, slices.Contains(email.To, "test3@example.com"))
assert.Contains(t, email.Data, `Subject: New "update" from "admin"`)
}
// now delete the script to generate an error
@ -4251,7 +4252,7 @@ func TestEventRuleProviderEvents(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "failure@example.com"))
assert.True(t, slices.Contains(email.To, "failure@example.com"))
assert.Contains(t, email.Data, `Subject: Failed "update" from "admin"`)
assert.Contains(t, email.Data, fmt.Sprintf("Object name: %s object type: folder", folder.Name))
lastReceivedEmail.reset()
@ -5306,7 +5307,7 @@ func TestBackupAsAttachment(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, renewalEvent))
assert.Contains(t, email.Data, `Domain: example.com`)
assert.Contains(t, email.Data, "Content-Type: application/json")
@ -5676,7 +5677,7 @@ func TestEventActionCompressQuotaErrors(t *testing.T) {
}, 3*time.Second, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, `Subject: "Compress failed"`)
assert.Contains(t, email.Data, common.ErrQuotaExceeded.Error())
// update quota size so the user is already overquota
@ -5691,7 +5692,7 @@ func TestEventActionCompressQuotaErrors(t *testing.T) {
}, 3*time.Second, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, `Subject: "Compress failed"`)
assert.Contains(t, email.Data, common.ErrQuotaExceeded.Error())
// remove the path to compress to trigger an error for size estimation
@ -5705,7 +5706,7 @@ func TestEventActionCompressQuotaErrors(t *testing.T) {
}, 3*time.Second, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, `Subject: "Compress failed"`)
assert.Contains(t, email.Data, "unable to estimate archive size")
}
@ -6041,7 +6042,7 @@ func TestEventActionEmailAttachments(t *testing.T) {
}, 1500*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, `Subject: "upload" from`)
assert.Contains(t, email.Data, "Content-Disposition: attachment")
}
@ -6218,7 +6219,7 @@ func TestEventActionsRetentionReports(t *testing.T) {
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "upload" from "%s"`, user.Username))
assert.Contains(t, email.Data, "Content-Disposition: attachment")
_, err = client.Stat(testDir)
@ -6391,7 +6392,7 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) {
}, 1500*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "first-upload" from "%s"`, user.Username))
lastReceivedEmail.reset()
// a new upload will not produce a new notification
@ -6414,7 +6415,7 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) {
}, 1500*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "first-download" from "%s"`, user.Username))
// download again
lastReceivedEmail.reset()
@ -6510,7 +6511,7 @@ func TestEventRuleRenameEvent(t *testing.T) {
}, 1500*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "rename" from "%s"`, user.Username))
assert.Contains(t, email.Data, "Content-Type: text/html")
assert.Contains(t, email.Data, fmt.Sprintf("Target path %q", path.Join("/subdir", testFileName)))
@ -6644,7 +6645,7 @@ func TestEventRuleIDPLogin(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, common.IDPLoginUser))
assert.Contains(t, email.Data, username)
assert.Contains(t, email.Data, custom1)
@ -6708,7 +6709,7 @@ func TestEventRuleIDPLogin(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, common.IDPLoginAdmin))
assert.Contains(t, email.Data, username)
assert.Contains(t, email.Data, custom1)
@ -6900,7 +6901,7 @@ func TestEventRuleEmailField(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, user.Email))
assert.True(t, slices.Contains(email.To, user.Email))
assert.Contains(t, email.Data, `Subject: "add" from "admin"`)
// if we add a user without email the notification will fail
@ -6914,7 +6915,7 @@ func TestEventRuleEmailField(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "failure@example.com"))
assert.True(t, slices.Contains(email.To, "failure@example.com"))
assert.Contains(t, email.Data, `no recipient addresses set`)
conn, client, err := getSftpClient(user)
@ -6931,7 +6932,7 @@ func TestEventRuleEmailField(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, user.Email))
assert.True(t, slices.Contains(email.To, user.Email))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "mkdir" from "%s"`, user.Username))
}
@ -7038,7 +7039,7 @@ func TestEventRuleCertificate(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, renewalEvent))
assert.Contains(t, email.Data, "Content-Type: text/plain")
assert.Contains(t, email.Data, `Domain: example.com Timestamp`)
@ -7058,7 +7059,7 @@ func TestEventRuleCertificate(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email = lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "test@example.com"))
assert.True(t, slices.Contains(email.To, "test@example.com"))
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s KO"`, renewalEvent))
assert.Contains(t, email.Data, `Domain: example.com Timestamp`)
assert.Contains(t, email.Data, errRenew.Error())
@ -7184,8 +7185,8 @@ func TestEventRuleIPBlocked(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 2)
assert.True(t, util.Contains(email.To, "test3@example.com"))
assert.True(t, util.Contains(email.To, "test4@example.com"))
assert.True(t, slices.Contains(email.To, "test3@example.com"))
assert.True(t, slices.Contains(email.To, "test4@example.com"))
assert.Contains(t, email.Data, `Subject: New "IP Blocked"`)
err = dataprovider.DeleteEventRule(rule1.Name, "", "", "")
@ -8357,7 +8358,7 @@ func TestSFTPLoopError(t *testing.T) {
}, 3000*time.Millisecond, 100*time.Millisecond)
email := lastReceivedEmail.get()
assert.Len(t, email.To, 1)
assert.True(t, util.Contains(email.To, "failure@example.com"))
assert.True(t, slices.Contains(email.To, "failure@example.com"))
assert.Contains(t, email.Data, `Subject: Failed action`)
user1.VirtualFolders[0].FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword)

View file

@ -17,6 +17,7 @@ package common
import (
"errors"
"fmt"
"slices"
"sort"
"sync"
"sync/atomic"
@ -94,7 +95,7 @@ func (r *RateLimiterConfig) validate() error {
}
r.Protocols = util.RemoveDuplicates(r.Protocols, true)
for _, protocol := range r.Protocols {
if !util.Contains(rateLimiterProtocolValues, protocol) {
if !slices.Contains(rateLimiterProtocolValues, protocol) {
return fmt.Errorf("invalid protocol %q", protocol)
}
}

View file

@ -25,6 +25,7 @@ import (
"math/rand"
"os"
"path/filepath"
"slices"
"sync"
"github.com/drakkan/sftpgo/v2/internal/logger"
@ -96,7 +97,7 @@ func (m *CertManager) loadCertificates() error {
}
logger.Debug(m.logSender, "", "TLS certificate %q successfully loaded, id %v", keyPair.Cert, keyPair.ID)
certs[keyPair.ID] = &newCert
if !util.Contains(m.monitorList, keyPair.Cert) {
if !slices.Contains(m.monitorList, keyPair.Cert) {
m.monitorList = append(m.monitorList, keyPair.Cert)
}
}
@ -190,7 +191,7 @@ func (m *CertManager) LoadCRLs() error {
logger.Debug(m.logSender, "", "CRL %q successfully loaded", revocationList)
crls = append(crls, crl)
if !util.Contains(m.monitorList, revocationList) {
if !slices.Contains(m.monitorList, revocationList) {
m.monitorList = append(m.monitorList, revocationList)
}
}

View file

@ -20,6 +20,7 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
@ -716,7 +717,7 @@ func checkOverrideDefaultSettings() {
}
}
if util.Contains(viper.AllKeys(), "mfa.totp") {
if slices.Contains(viper.AllKeys(), "mfa.totp") {
globalConf.MFAConfig.TOTP = nil
}
}

View file

@ -19,6 +19,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"slices"
"testing"
"github.com/sftpgo/sdk/kms"
@ -36,7 +37,6 @@ import (
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/sftpd"
"github.com/drakkan/sftpgo/v2/internal/smtp"
"github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/webdavd"
)
@ -679,8 +679,8 @@ func TestPluginsFromEnv(t *testing.T) {
pluginConf := pluginsConf[0]
require.Equal(t, "notifier", pluginConf.Type)
require.Len(t, pluginConf.NotifierOptions.FsEvents, 2)
require.True(t, util.Contains(pluginConf.NotifierOptions.FsEvents, "upload"))
require.True(t, util.Contains(pluginConf.NotifierOptions.FsEvents, "download"))
require.True(t, slices.Contains(pluginConf.NotifierOptions.FsEvents, "upload"))
require.True(t, slices.Contains(pluginConf.NotifierOptions.FsEvents, "download"))
require.Len(t, pluginConf.NotifierOptions.ProviderEvents, 2)
require.Equal(t, "add", pluginConf.NotifierOptions.ProviderEvents[0])
require.Equal(t, "update", pluginConf.NotifierOptions.ProviderEvents[1])
@ -729,8 +729,8 @@ func TestPluginsFromEnv(t *testing.T) {
pluginConf = pluginsConf[0]
require.Equal(t, "notifier", pluginConf.Type)
require.Len(t, pluginConf.NotifierOptions.FsEvents, 2)
require.True(t, util.Contains(pluginConf.NotifierOptions.FsEvents, "upload"))
require.True(t, util.Contains(pluginConf.NotifierOptions.FsEvents, "download"))
require.True(t, slices.Contains(pluginConf.NotifierOptions.FsEvents, "upload"))
require.True(t, slices.Contains(pluginConf.NotifierOptions.FsEvents, "download"))
require.Len(t, pluginConf.NotifierOptions.ProviderEvents, 2)
require.Equal(t, "add", pluginConf.NotifierOptions.ProviderEvents[0])
require.Equal(t, "update", pluginConf.NotifierOptions.ProviderEvents[1])
@ -787,8 +787,8 @@ func TestRateLimitersFromEnv(t *testing.T) {
require.Equal(t, 2, limiters[0].Type)
protocols := limiters[0].Protocols
require.Len(t, protocols, 2)
require.True(t, util.Contains(protocols, common.ProtocolFTP))
require.True(t, util.Contains(protocols, common.ProtocolSSH))
require.True(t, slices.Contains(protocols, common.ProtocolFTP))
require.True(t, slices.Contains(protocols, common.ProtocolSSH))
require.True(t, limiters[0].GenerateDefenderEvents)
require.Equal(t, 50, limiters[0].EntriesSoftLimit)
require.Equal(t, 100, limiters[0].EntriesHardLimit)
@ -799,10 +799,10 @@ func TestRateLimitersFromEnv(t *testing.T) {
require.Equal(t, 2, limiters[1].Type)
protocols = limiters[1].Protocols
require.Len(t, protocols, 4)
require.True(t, util.Contains(protocols, common.ProtocolFTP))
require.True(t, util.Contains(protocols, common.ProtocolSSH))
require.True(t, util.Contains(protocols, common.ProtocolWebDAV))
require.True(t, util.Contains(protocols, common.ProtocolHTTP))
require.True(t, slices.Contains(protocols, common.ProtocolFTP))
require.True(t, slices.Contains(protocols, common.ProtocolSSH))
require.True(t, slices.Contains(protocols, common.ProtocolWebDAV))
require.True(t, slices.Contains(protocols, common.ProtocolHTTP))
require.False(t, limiters[1].GenerateDefenderEvents)
require.Equal(t, 100, limiters[1].EntriesSoftLimit)
require.Equal(t, 150, limiters[1].EntriesHardLimit)

View file

@ -21,6 +21,7 @@ import (
"net/url"
"os/exec"
"path/filepath"
"slices"
"strings"
"time"
@ -78,8 +79,8 @@ func executeAction(operation, executor, ip, objectType, objectName, role string,
if config.Actions.Hook == "" {
return
}
if !util.Contains(config.Actions.ExecuteOn, operation) ||
!util.Contains(config.Actions.ExecuteFor, objectType) {
if !slices.Contains(config.Actions.ExecuteOn, operation) ||
!slices.Contains(config.Actions.ExecuteFor, objectType) {
return
}

View file

@ -20,6 +20,7 @@ import (
"fmt"
"net"
"os"
"slices"
"strconv"
"strings"
@ -96,7 +97,7 @@ func (c *AdminTOTPConfig) validate(username string) error {
if c.ConfigName == "" {
return util.NewValidationError("totp: config name is mandatory")
}
if !util.Contains(mfa.GetAvailableTOTPConfigNames(), c.ConfigName) {
if !slices.Contains(mfa.GetAvailableTOTPConfigNames(), c.ConfigName) {
return util.NewValidationError(fmt.Sprintf("totp: config name %q not found", c.ConfigName))
}
if c.Secret.IsEmpty() {
@ -337,15 +338,15 @@ func (a *Admin) validatePermissions() error {
util.I18nErrorPermissionsRequired,
)
}
if util.Contains(a.Permissions, PermAdminAny) {
if slices.Contains(a.Permissions, PermAdminAny) {
a.Permissions = []string{PermAdminAny}
}
for _, perm := range a.Permissions {
if !util.Contains(validAdminPerms, perm) {
if !slices.Contains(validAdminPerms, perm) {
return util.NewValidationError(fmt.Sprintf("invalid permission: %q", perm))
}
if a.Role != "" {
if util.Contains(forbiddenPermsForRoleAdmins, perm) {
if slices.Contains(forbiddenPermsForRoleAdmins, perm) {
deniedPerms := strings.Join(forbiddenPermsForRoleAdmins, ",")
return util.NewI18nError(
util.NewValidationError(fmt.Sprintf("a role admin cannot have the following permissions: %q", deniedPerms)),
@ -559,10 +560,10 @@ func (a *Admin) SetNilSecretsIfEmpty() {
// HasPermission returns true if the admin has the specified permission
func (a *Admin) HasPermission(perm string) bool {
if util.Contains(a.Permissions, PermAdminAny) {
if slices.Contains(a.Permissions, PermAdminAny) {
return true
}
return util.Contains(a.Permissions, perm)
return slices.Contains(a.Permissions, perm)
}
// GetAllowedIPAsString returns the allowed IP as comma separated string

View file

@ -25,6 +25,7 @@ import (
"fmt"
"net/netip"
"path/filepath"
"slices"
"sort"
"time"
@ -3320,7 +3321,7 @@ func (p *BoltProvider) addAdminToRole(username, roleName string, bucket *bolt.Bu
if err != nil {
return err
}
if !util.Contains(role.Admins, username) {
if !slices.Contains(role.Admins, username) {
role.Admins = append(role.Admins, username)
buf, err := json.Marshal(role)
if err != nil {
@ -3345,7 +3346,7 @@ func (p *BoltProvider) removeAdminFromRole(username, roleName string, bucket *bo
if err != nil {
return err
}
if util.Contains(role.Admins, username) {
if slices.Contains(role.Admins, username) {
var admins []string
for _, admin := range role.Admins {
if admin != username {
@ -3375,7 +3376,7 @@ func (p *BoltProvider) addUserToRole(username, roleName string, bucket *bolt.Buc
if err != nil {
return err
}
if !util.Contains(role.Users, username) {
if !slices.Contains(role.Users, username) {
role.Users = append(role.Users, username)
buf, err := json.Marshal(role)
if err != nil {
@ -3400,7 +3401,7 @@ func (p *BoltProvider) removeUserFromRole(username, roleName string, bucket *bol
if err != nil {
return err
}
if util.Contains(role.Users, username) {
if slices.Contains(role.Users, username) {
var users []string
for _, user := range role.Users {
if user != username {
@ -3428,7 +3429,7 @@ func (p *BoltProvider) addRuleToActionMapping(ruleName, actionName string, bucke
if err != nil {
return err
}
if !util.Contains(action.Rules, ruleName) {
if !slices.Contains(action.Rules, ruleName) {
action.Rules = append(action.Rules, ruleName)
buf, err := json.Marshal(action)
if err != nil {
@ -3450,7 +3451,7 @@ func (p *BoltProvider) removeRuleFromActionMapping(ruleName, actionName string,
if err != nil {
return err
}
if util.Contains(action.Rules, ruleName) {
if slices.Contains(action.Rules, ruleName) {
var rules []string
for _, r := range action.Rules {
if r != ruleName {
@ -3477,7 +3478,7 @@ func (p *BoltProvider) addUserToGroupMapping(username, groupname string, bucket
if err != nil {
return err
}
if !util.Contains(group.Users, username) {
if !slices.Contains(group.Users, username) {
group.Users = append(group.Users, username)
buf, err := json.Marshal(group)
if err != nil {
@ -3522,7 +3523,7 @@ func (p *BoltProvider) addAdminToGroupMapping(username, groupname string, bucket
if err != nil {
return err
}
if !util.Contains(group.Admins, username) {
if !slices.Contains(group.Admins, username) {
group.Admins = append(group.Admins, username)
buf, err := json.Marshal(group)
if err != nil {
@ -3593,11 +3594,11 @@ func (p *BoltProvider) addRelationToFolderMapping(folderName string, user *User,
return err
}
updated := false
if user != nil && !util.Contains(folder.Users, user.Username) {
if user != nil && !slices.Contains(folder.Users, user.Username) {
folder.Users = append(folder.Users, user.Username)
updated = true
}
if group != nil && !util.Contains(folder.Groups, group.Name) {
if group != nil && !slices.Contains(folder.Groups, group.Name) {
folder.Groups = append(folder.Groups, group.Name)
updated = true
}

View file

@ -20,6 +20,7 @@ import (
"fmt"
"image/png"
"net/url"
"slices"
"golang.org/x/crypto/ssh"
@ -105,7 +106,7 @@ func (c *SFTPDConfigs) validate() error {
if algo == ssh.CertAlgoRSAv01 {
continue
}
if !util.Contains(supportedHostKeyAlgos, algo) {
if !slices.Contains(supportedHostKeyAlgos, algo) {
return util.NewValidationError(fmt.Sprintf("unsupported host key algorithm %q", algo))
}
hostKeyAlgos = append(hostKeyAlgos, algo)
@ -116,24 +117,24 @@ func (c *SFTPDConfigs) validate() error {
if algo == "diffie-hellman-group18-sha512" || algo == ssh.KeyExchangeDHGEXSHA256 {
continue
}
if !util.Contains(supportedKexAlgos, algo) {
if !slices.Contains(supportedKexAlgos, algo) {
return util.NewValidationError(fmt.Sprintf("unsupported KEX algorithm %q", algo))
}
kexAlgos = append(kexAlgos, algo)
}
c.KexAlgorithms = kexAlgos
for _, cipher := range c.Ciphers {
if !util.Contains(supportedCiphers, cipher) {
if !slices.Contains(supportedCiphers, cipher) {
return util.NewValidationError(fmt.Sprintf("unsupported cipher %q", cipher))
}
}
for _, mac := range c.MACs {
if !util.Contains(supportedMACs, mac) {
if !slices.Contains(supportedMACs, mac) {
return util.NewValidationError(fmt.Sprintf("unsupported MAC algorithm %q", mac))
}
}
for _, algo := range c.PublicKeyAlgos {
if !util.Contains(supportedPublicKeyAlgos, algo) {
if !slices.Contains(supportedPublicKeyAlgos, algo) {
return util.NewValidationError(fmt.Sprintf("unsupported public key algorithm %q", algo))
}
}

View file

@ -44,6 +44,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@ -519,7 +520,7 @@ type Config struct {
// GetShared returns the provider share mode.
// This method is called before the provider is initialized
func (c *Config) GetShared() int {
if !util.Contains(sharedProviders, c.Driver) {
if !slices.Contains(sharedProviders, c.Driver) {
return 0
}
return c.IsShared
@ -885,7 +886,7 @@ func SetTempPath(fsPath string) {
}
func checkSharedMode() {
if !util.Contains(sharedProviders, config.Driver) {
if !slices.Contains(sharedProviders, config.Driver) {
config.IsShared = 0
}
}
@ -1714,7 +1715,7 @@ func IPListEntryExists(ipOrNet string, listType IPListType) (IPListEntry, error)
// GetIPListEntries returns the IP list entries applying the specified criteria and search limit
func GetIPListEntries(listType IPListType, filter, from, order string, limit int) ([]IPListEntry, error) {
if !util.Contains(supportedIPListType, listType) {
if !slices.Contains(supportedIPListType, listType) {
return nil, util.NewValidationError(fmt.Sprintf("invalid list type %d", listType))
}
return provider.getIPListEntries(listType, filter, from, order, limit)
@ -2373,7 +2374,7 @@ func GetFolders(limit, offset int, order string, minimal bool) ([]vfs.BaseVirtua
}
func dumpUsers(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeUsers) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeUsers) {
users, err := provider.dumpUsers()
if err != nil {
return err
@ -2384,7 +2385,7 @@ func dumpUsers(data *BackupData, scopes []string) error {
}
func dumpFolders(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeFolders) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeFolders) {
folders, err := provider.dumpFolders()
if err != nil {
return err
@ -2395,7 +2396,7 @@ func dumpFolders(data *BackupData, scopes []string) error {
}
func dumpGroups(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeGroups) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeGroups) {
groups, err := provider.dumpGroups()
if err != nil {
return err
@ -2406,7 +2407,7 @@ func dumpGroups(data *BackupData, scopes []string) error {
}
func dumpAdmins(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeAdmins) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeAdmins) {
admins, err := provider.dumpAdmins()
if err != nil {
return err
@ -2417,7 +2418,7 @@ func dumpAdmins(data *BackupData, scopes []string) error {
}
func dumpAPIKeys(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeAPIKeys) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeAPIKeys) {
apiKeys, err := provider.dumpAPIKeys()
if err != nil {
return err
@ -2428,7 +2429,7 @@ func dumpAPIKeys(data *BackupData, scopes []string) error {
}
func dumpShares(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeShares) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeShares) {
shares, err := provider.dumpShares()
if err != nil {
return err
@ -2439,7 +2440,7 @@ func dumpShares(data *BackupData, scopes []string) error {
}
func dumpActions(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeActions) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeActions) {
actions, err := provider.dumpEventActions()
if err != nil {
return err
@ -2450,7 +2451,7 @@ func dumpActions(data *BackupData, scopes []string) error {
}
func dumpRules(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeRules) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeRules) {
rules, err := provider.dumpEventRules()
if err != nil {
return err
@ -2461,7 +2462,7 @@ func dumpRules(data *BackupData, scopes []string) error {
}
func dumpRoles(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeRoles) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeRoles) {
roles, err := provider.dumpRoles()
if err != nil {
return err
@ -2472,7 +2473,7 @@ func dumpRoles(data *BackupData, scopes []string) error {
}
func dumpIPLists(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeIPLists) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeIPLists) {
ipLists, err := provider.dumpIPListEntries()
if err != nil {
return err
@ -2483,7 +2484,7 @@ func dumpIPLists(data *BackupData, scopes []string) error {
}
func dumpConfigs(data *BackupData, scopes []string) error {
if len(scopes) == 0 || util.Contains(scopes, DumpScopeConfigs) {
if len(scopes) == 0 || slices.Contains(scopes, DumpScopeConfigs) {
configs, err := provider.getConfigs()
if err != nil {
return err
@ -2787,7 +2788,7 @@ func validateUserTOTPConfig(c *UserTOTPConfig, username string) error {
if c.ConfigName == "" {
return util.NewValidationError("totp: config name is mandatory")
}
if !util.Contains(mfa.GetAvailableTOTPConfigNames(), c.ConfigName) {
if !slices.Contains(mfa.GetAvailableTOTPConfigNames(), c.ConfigName) {
return util.NewValidationError(fmt.Sprintf("totp: config name %q not found", c.ConfigName))
}
if c.Secret.IsEmpty() {
@ -2803,7 +2804,7 @@ func validateUserTOTPConfig(c *UserTOTPConfig, username string) error {
return util.NewValidationError("totp: specify at least one protocol")
}
for _, protocol := range c.Protocols {
if !util.Contains(MFAProtocols, protocol) {
if !slices.Contains(MFAProtocols, protocol) {
return util.NewValidationError(fmt.Sprintf("totp: invalid protocol %q", protocol))
}
}
@ -2836,7 +2837,7 @@ func validateUserPermissions(permsToCheck map[string][]string) (map[string][]str
return permissions, util.NewValidationError("invalid permissions")
}
for _, p := range perms {
if !util.Contains(ValidPerms, p) {
if !slices.Contains(ValidPerms, p) {
return permissions, util.NewValidationError(fmt.Sprintf("invalid permission: %q", p))
}
}
@ -2850,7 +2851,7 @@ func validateUserPermissions(permsToCheck map[string][]string) (map[string][]str
if dir != cleanedDir && cleanedDir == "/" {
return permissions, util.NewValidationError(fmt.Sprintf("cannot set permissions for invalid subdirectory: %q is an alias for \"/\"", dir))
}
if util.Contains(perms, PermAny) {
if slices.Contains(perms, PermAny) {
permissions[cleanedDir] = []string{PermAny}
} else {
permissions[cleanedDir] = util.RemoveDuplicates(perms, false)
@ -2926,7 +2927,7 @@ func validateFiltersPatternExtensions(baseFilters *sdk.BaseUserFilters) error {
util.I18nErrorFilePatternPathInvalid,
)
}
if util.Contains(filteredPaths, cleanedPath) {
if slices.Contains(filteredPaths, cleanedPath) {
return util.NewI18nError(
util.NewValidationError(fmt.Sprintf("duplicate file patterns filter for path %q", f.Path)),
util.I18nErrorFilePatternDuplicated,
@ -3045,13 +3046,13 @@ func validateFilterProtocols(filters *sdk.BaseUserFilters) error {
return util.NewValidationError("invalid denied_protocols")
}
for _, p := range filters.DeniedProtocols {
if !util.Contains(ValidProtocols, p) {
if !slices.Contains(ValidProtocols, p) {
return util.NewValidationError(fmt.Sprintf("invalid denied protocol %q", p))
}
}
for _, p := range filters.TwoFactorAuthProtocols {
if !util.Contains(MFAProtocols, p) {
if !slices.Contains(MFAProtocols, p) {
return util.NewValidationError(fmt.Sprintf("invalid two factor protocol %q", p))
}
}
@ -3107,7 +3108,7 @@ func validateBaseFilters(filters *sdk.BaseUserFilters) error {
return util.NewValidationError("invalid denied_login_methods")
}
for _, loginMethod := range filters.DeniedLoginMethods {
if !util.Contains(ValidLoginMethods, loginMethod) {
if !slices.Contains(ValidLoginMethods, loginMethod) {
return util.NewValidationError(fmt.Sprintf("invalid login method: %q", loginMethod))
}
}
@ -3115,7 +3116,7 @@ func validateBaseFilters(filters *sdk.BaseUserFilters) error {
return err
}
if filters.TLSUsername != "" {
if !util.Contains(validTLSUsernames, string(filters.TLSUsername)) {
if !slices.Contains(validTLSUsernames, string(filters.TLSUsername)) {
return util.NewValidationError(fmt.Sprintf("invalid TLS username: %q", filters.TLSUsername))
}
}
@ -3125,7 +3126,7 @@ func validateBaseFilters(filters *sdk.BaseUserFilters) error {
}
filters.TLSCerts = certs
for _, opts := range filters.WebClient {
if !util.Contains(sdk.WebClientOptions, opts) {
if !slices.Contains(sdk.WebClientOptions, opts) {
return util.NewValidationError(fmt.Sprintf("invalid web client options %q", opts))
}
}
@ -3193,19 +3194,19 @@ func validateAccessTimeFilters(filters *sdk.BaseUserFilters) error {
}
func validateCombinedUserFilters(user *User) error {
if user.Filters.TOTPConfig.Enabled && util.Contains(user.Filters.WebClient, sdk.WebClientMFADisabled) {
if user.Filters.TOTPConfig.Enabled && slices.Contains(user.Filters.WebClient, sdk.WebClientMFADisabled) {
return util.NewI18nError(
util.NewValidationError("two-factor authentication cannot be disabled for a user with an active configuration"),
util.I18nErrorDisableActive2FA,
)
}
if user.Filters.RequirePasswordChange && util.Contains(user.Filters.WebClient, sdk.WebClientPasswordChangeDisabled) {
if user.Filters.RequirePasswordChange && slices.Contains(user.Filters.WebClient, sdk.WebClientPasswordChangeDisabled) {
return util.NewI18nError(
util.NewValidationError("you cannot require password change and at the same time disallow it"),
util.I18nErrorPwdChangeConflict,
)
}
if len(user.Filters.TwoFactorAuthProtocols) > 0 && util.Contains(user.Filters.WebClient, sdk.WebClientMFADisabled) {
if len(user.Filters.TwoFactorAuthProtocols) > 0 && slices.Contains(user.Filters.WebClient, sdk.WebClientMFADisabled) {
return util.NewI18nError(
util.NewValidationError("you cannot require two-factor authentication and at the same time disallow it"),
util.I18nError2FAConflict,
@ -3526,7 +3527,7 @@ func checkUserPasscode(user *User, password, protocol string) (string, error) {
if user.Filters.TOTPConfig.Enabled {
switch protocol {
case protocolFTP:
if util.Contains(user.Filters.TOTPConfig.Protocols, protocol) {
if slices.Contains(user.Filters.TOTPConfig.Protocols, protocol) {
// the TOTP passcode has six digits
pwdLen := len(password)
if pwdLen < 7 {
@ -3732,7 +3733,7 @@ func doBuiltinKeyboardInteractiveAuth(user *User, client ssh.KeyboardInteractive
if err := user.LoadAndApplyGroupSettings(); err != nil {
return 0, err
}
hasSecondFactor := user.Filters.TOTPConfig.Enabled && util.Contains(user.Filters.TOTPConfig.Protocols, protocolSSH)
hasSecondFactor := user.Filters.TOTPConfig.Enabled && slices.Contains(user.Filters.TOTPConfig.Protocols, protocolSSH)
if !isPartialAuth || !hasSecondFactor {
answers, err := client("", "", []string{"Password: "}, []bool{false})
if err != nil {
@ -3750,7 +3751,7 @@ func doBuiltinKeyboardInteractiveAuth(user *User, client ssh.KeyboardInteractive
}
func checkKeyboardInteractiveSecondFactor(user *User, client ssh.KeyboardInteractiveChallenge, protocol string) (int, error) {
if !user.Filters.TOTPConfig.Enabled || !util.Contains(user.Filters.TOTPConfig.Protocols, protocolSSH) {
if !user.Filters.TOTPConfig.Enabled || !slices.Contains(user.Filters.TOTPConfig.Protocols, protocolSSH) {
return 1, nil
}
err := user.Filters.TOTPConfig.Secret.TryDecrypt()
@ -3874,7 +3875,7 @@ func getKeyboardInteractiveAnswers(client ssh.KeyboardInteractiveChallenge, resp
}
if len(answers) == 1 && response.CheckPwd > 0 {
if response.CheckPwd == 2 {
if !user.Filters.TOTPConfig.Enabled || !util.Contains(user.Filters.TOTPConfig.Protocols, protocolSSH) {
if !user.Filters.TOTPConfig.Enabled || !slices.Contains(user.Filters.TOTPConfig.Protocols, protocolSSH) {
providerLog(logger.LevelInfo, "keyboard interactive auth error: unable to check TOTP passcode, TOTP is not enabled for user %q",
user.Username)
return answers, errors.New("TOTP not enabled for SSH protocol")
@ -4640,7 +4641,7 @@ func getConfigPath(name, configDir string) string {
}
func checkReservedUsernames(username string) error {
if util.Contains(reservedUsers, username) {
if slices.Contains(reservedUsers, username) {
return util.NewValidationError("this username is reserved")
}
return nil

View file

@ -23,6 +23,7 @@ import (
"net/http"
"path"
"path/filepath"
"slices"
"strings"
"time"
@ -60,7 +61,7 @@ var (
)
func isActionTypeValid(action int) bool {
return util.Contains(supportedEventActions, action)
return slices.Contains(supportedEventActions, action)
}
func getActionTypeAsString(action int) string {
@ -115,7 +116,7 @@ var (
)
func isEventTriggerValid(trigger int) bool {
return util.Contains(supportedEventTriggers, trigger)
return slices.Contains(supportedEventTriggers, trigger)
}
func getTriggerTypeAsString(trigger int) string {
@ -169,7 +170,7 @@ var (
)
func isFilesystemActionValid(value int) bool {
return util.Contains(supportedFsActions, value)
return slices.Contains(supportedFsActions, value)
}
func getFsActionTypeAsString(value int) string {
@ -380,7 +381,7 @@ func (c *EventActionHTTPConfig) validate(additionalData string) error {
return util.NewValidationError(fmt.Sprintf("could not encrypt HTTP password: %v", err))
}
}
if !util.Contains(SupportedHTTPActionMethods, c.Method) {
if !slices.Contains(SupportedHTTPActionMethods, c.Method) {
return util.NewValidationError(fmt.Sprintf("unsupported HTTP method: %s", c.Method))
}
for _, kv := range c.QueryParameters {
@ -1280,7 +1281,7 @@ func (a *EventAction) validateAssociation(trigger int, fsEvents []string) error
}
if trigger == EventTriggerFsEvent {
for _, ev := range fsEvents {
if !util.Contains(allowedSyncFsEvents, ev) {
if !slices.Contains(allowedSyncFsEvents, ev) {
return util.NewI18nError(
util.NewValidationError("sync execution is only supported for upload and pre-* events"),
util.I18nErrorEvSyncUnsupportedFs,
@ -1361,12 +1362,12 @@ func (f *ConditionOptions) validate() error {
}
for _, p := range f.Protocols {
if !util.Contains(SupportedRuleConditionProtocols, p) {
if !slices.Contains(SupportedRuleConditionProtocols, p) {
return util.NewValidationError(fmt.Sprintf("unsupported rule condition protocol: %q", p))
}
}
for _, p := range f.ProviderObjects {
if !util.Contains(SupporteRuleConditionProviderObjects, p) {
if !slices.Contains(SupporteRuleConditionProviderObjects, p) {
return util.NewValidationError(fmt.Sprintf("unsupported provider object: %q", p))
}
}
@ -1468,7 +1469,7 @@ func (c *EventConditions) validate(trigger int) error {
)
}
for _, ev := range c.FsEvents {
if !util.Contains(SupportedFsEvents, ev) {
if !slices.Contains(SupportedFsEvents, ev) {
return util.NewValidationError(fmt.Sprintf("unsupported fs event: %q", ev))
}
}
@ -1488,7 +1489,7 @@ func (c *EventConditions) validate(trigger int) error {
)
}
for _, ev := range c.ProviderEvents {
if !util.Contains(SupportedProviderEvents, ev) {
if !slices.Contains(SupportedProviderEvents, ev) {
return util.NewValidationError(fmt.Sprintf("unsupported provider event: %q", ev))
}
}
@ -1537,7 +1538,7 @@ func (c *EventConditions) validate(trigger int) error {
c.Options.MinFileSize = 0
c.Options.MaxFileSize = 0
c.Schedules = nil
if !util.Contains(supportedIDPLoginEvents, c.IDPLoginEvent) {
if !slices.Contains(supportedIDPLoginEvents, c.IDPLoginEvent) {
return util.NewValidationError(fmt.Sprintf("invalid Identity Provider login event %d", c.IDPLoginEvent))
}
default:
@ -1690,7 +1691,7 @@ func (r *EventRule) validateMandatorySyncActions() error {
return nil
}
for _, ev := range r.Conditions.FsEvents {
if util.Contains(mandatorySyncFsEvents, ev) {
if slices.Contains(mandatorySyncFsEvents, ev) {
return util.NewI18nError(
util.NewValidationError(fmt.Sprintf("event %q requires at least a sync action", ev)),
util.I18nErrorRuleSyncActionRequired,
@ -1708,7 +1709,7 @@ func (r *EventRule) checkIPBlockedAndCertificateActions() error {
ActionTypeDataRetentionCheck, ActionTypeFilesystem, ActionTypePasswordExpirationCheck,
ActionTypeUserExpirationCheck}
for _, action := range r.Actions {
if util.Contains(unavailableActions, action.Type) {
if slices.Contains(unavailableActions, action.Type) {
return fmt.Errorf("action %q, type %q is not supported for event trigger %q",
action.Name, getActionTypeAsString(action.Type), getTriggerTypeAsString(r.Trigger))
}
@ -1724,7 +1725,7 @@ func (r *EventRule) checkProviderEventActions(providerObjectType string) error {
ActionTypeDataRetentionCheck, ActionTypeFilesystem,
ActionTypePasswordExpirationCheck, ActionTypeUserExpirationCheck}
for _, action := range r.Actions {
if util.Contains(userSpecificActions, action.Type) && providerObjectType != actionObjectUser {
if slices.Contains(userSpecificActions, action.Type) && providerObjectType != actionObjectUser {
return fmt.Errorf("action %q, type %q is only supported for provider user events",
action.Name, getActionTypeAsString(action.Type))
}

View file

@ -19,6 +19,7 @@ import (
"fmt"
"net"
"net/netip"
"slices"
"strings"
"sync"
"sync/atomic"
@ -85,7 +86,7 @@ var (
// CheckIPListType returns an error if the provided IP list type is not valid
func CheckIPListType(t IPListType) error {
if !util.Contains(supportedIPListType, t) {
if !slices.Contains(supportedIPListType, t) {
return util.NewValidationError(fmt.Sprintf("invalid list type %d", t))
}
return nil

View file

@ -22,6 +22,7 @@ import (
"net/netip"
"os"
"path/filepath"
"slices"
"sort"
"sync"
"time"
@ -1210,7 +1211,7 @@ func (p *MemoryProvider) addRuleToActionMapping(ruleName, actionName string) err
if err != nil {
return util.NewGenericError(fmt.Sprintf("action %q does not exist", actionName))
}
if !util.Contains(a.Rules, ruleName) {
if !slices.Contains(a.Rules, ruleName) {
a.Rules = append(a.Rules, ruleName)
p.dbHandle.actions[actionName] = a
}
@ -1223,7 +1224,7 @@ func (p *MemoryProvider) removeRuleFromActionMapping(ruleName, actionName string
providerLog(logger.LevelWarn, "action %q does not exist, cannot remove from mapping", actionName)
return
}
if util.Contains(a.Rules, ruleName) {
if slices.Contains(a.Rules, ruleName) {
var rules []string
for _, r := range a.Rules {
if r != ruleName {
@ -1240,7 +1241,7 @@ func (p *MemoryProvider) addAdminToGroupMapping(username, groupname string) erro
if err != nil {
return err
}
if !util.Contains(g.Admins, username) {
if !slices.Contains(g.Admins, username) {
g.Admins = append(g.Admins, username)
p.dbHandle.groups[groupname] = g
}
@ -1283,7 +1284,7 @@ func (p *MemoryProvider) addUserToGroupMapping(username, groupname string) error
if err != nil {
return err
}
if !util.Contains(g.Users, username) {
if !slices.Contains(g.Users, username) {
g.Users = append(g.Users, username)
p.dbHandle.groups[groupname] = g
}
@ -1313,7 +1314,7 @@ func (p *MemoryProvider) addAdminToRole(username, role string) error {
if err != nil {
return fmt.Errorf("%w: role %q does not exist", ErrForeignKeyViolated, role)
}
if !util.Contains(r.Admins, username) {
if !slices.Contains(r.Admins, username) {
r.Admins = append(r.Admins, username)
p.dbHandle.roles[role] = r
}
@ -1347,7 +1348,7 @@ func (p *MemoryProvider) addUserToRole(username, role string) error {
if err != nil {
return fmt.Errorf("%w: role %q does not exist", ErrForeignKeyViolated, role)
}
if !util.Contains(r.Users, username) {
if !slices.Contains(r.Users, username) {
r.Users = append(r.Users, username)
p.dbHandle.roles[role] = r
}
@ -1378,7 +1379,7 @@ func (p *MemoryProvider) addUserToFolderMapping(username, foldername string) err
if err != nil {
return util.NewGenericError(fmt.Sprintf("unable to get folder %q: %v", foldername, err))
}
if !util.Contains(f.Users, username) {
if !slices.Contains(f.Users, username) {
f.Users = append(f.Users, username)
p.dbHandle.vfolders[foldername] = f
}
@ -1390,7 +1391,7 @@ func (p *MemoryProvider) addGroupToFolderMapping(name, foldername string) error
if err != nil {
return util.NewGenericError(fmt.Sprintf("unable to get folder %q: %v", foldername, err))
}
if !util.Contains(f.Groups, name) {
if !slices.Contains(f.Groups, name) {
f.Groups = append(f.Groups, name)
p.dbHandle.vfolders[foldername] = f
}

View file

@ -24,6 +24,7 @@ import (
"errors"
"fmt"
"net"
"slices"
"strconv"
"strings"
"time"
@ -305,7 +306,7 @@ func getPGSQLConnectionString(redactedPwd bool) string {
if config.DisableSNI {
connectionString += " sslsni=0"
}
if util.Contains(pgSQLTargetSessionAttrs, config.TargetSessionAttrs) {
if slices.Contains(pgSQLTargetSessionAttrs, config.TargetSessionAttrs) {
connectionString += fmt.Sprintf(" target_session_attrs='%s'", config.TargetSessionAttrs)
}
} else {

View file

@ -22,6 +22,7 @@ import (
"net"
"os"
"path"
"slices"
"strconv"
"strings"
"time"
@ -844,20 +845,20 @@ func (u *User) HasPermissionsInside(virtualPath string) bool {
// HasPerm returns true if the user has the given permission or any permission
func (u *User) HasPerm(permission, path string) bool {
perms := u.GetPermissionsForPath(path)
if util.Contains(perms, PermAny) {
if slices.Contains(perms, PermAny) {
return true
}
return util.Contains(perms, permission)
return slices.Contains(perms, permission)
}
// HasAnyPerm returns true if the user has at least one of the given permissions
func (u *User) HasAnyPerm(permissions []string, path string) bool {
perms := u.GetPermissionsForPath(path)
if util.Contains(perms, PermAny) {
if slices.Contains(perms, PermAny) {
return true
}
for _, permission := range permissions {
if util.Contains(perms, permission) {
if slices.Contains(perms, permission) {
return true
}
}
@ -867,11 +868,11 @@ func (u *User) HasAnyPerm(permissions []string, path string) bool {
// HasPerms returns true if the user has all the given permissions
func (u *User) HasPerms(permissions []string, path string) bool {
perms := u.GetPermissionsForPath(path)
if util.Contains(perms, PermAny) {
if slices.Contains(perms, PermAny) {
return true
}
for _, permission := range permissions {
if !util.Contains(perms, permission) {
if !slices.Contains(perms, permission) {
return false
}
}
@ -931,11 +932,11 @@ func (u *User) IsLoginMethodAllowed(loginMethod, protocol string) bool {
if len(u.Filters.DeniedLoginMethods) == 0 {
return true
}
if util.Contains(u.Filters.DeniedLoginMethods, loginMethod) {
if slices.Contains(u.Filters.DeniedLoginMethods, loginMethod) {
return false
}
if protocol == protocolSSH && loginMethod == LoginMethodPassword {
if util.Contains(u.Filters.DeniedLoginMethods, SSHLoginMethodPassword) {
if slices.Contains(u.Filters.DeniedLoginMethods, SSHLoginMethodPassword) {
return false
}
}
@ -969,10 +970,10 @@ func (u *User) IsPartialAuth() bool {
method == SSHLoginMethodPassword {
continue
}
if method == LoginMethodPassword && util.Contains(u.Filters.DeniedLoginMethods, SSHLoginMethodPassword) {
if method == LoginMethodPassword && slices.Contains(u.Filters.DeniedLoginMethods, SSHLoginMethodPassword) {
continue
}
if !util.Contains(SSHMultiStepsLoginMethods, method) {
if !slices.Contains(SSHMultiStepsLoginMethods, method) {
return false
}
}
@ -986,7 +987,7 @@ func (u *User) GetAllowedLoginMethods() []string {
if method == SSHLoginMethodPassword {
continue
}
if !util.Contains(u.Filters.DeniedLoginMethods, method) {
if !slices.Contains(u.Filters.DeniedLoginMethods, method) {
allowedMethods = append(allowedMethods, method)
}
}
@ -1056,7 +1057,7 @@ func (u *User) IsFileAllowed(virtualPath string) (bool, int) {
// CanManageMFA returns true if the user can add a multi-factor authentication configuration
func (u *User) CanManageMFA() bool {
if util.Contains(u.Filters.WebClient, sdk.WebClientMFADisabled) {
if slices.Contains(u.Filters.WebClient, sdk.WebClientMFADisabled) {
return false
}
return len(mfa.GetAvailableTOTPConfigs()) > 0
@ -1077,39 +1078,39 @@ func (u *User) skipExternalAuth() bool {
// CanManageShares returns true if the user can add, update and list shares
func (u *User) CanManageShares() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientSharesDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientSharesDisabled)
}
// CanResetPassword returns true if this user is allowed to reset its password
func (u *User) CanResetPassword() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientPasswordResetDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientPasswordResetDisabled)
}
// CanChangePassword returns true if this user is allowed to change its password
func (u *User) CanChangePassword() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientPasswordChangeDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientPasswordChangeDisabled)
}
// CanChangeAPIKeyAuth returns true if this user is allowed to enable/disable API key authentication
func (u *User) CanChangeAPIKeyAuth() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientAPIKeyAuthChangeDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientAPIKeyAuthChangeDisabled)
}
// CanChangeInfo returns true if this user is allowed to change its info such as email and description
func (u *User) CanChangeInfo() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientInfoChangeDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientInfoChangeDisabled)
}
// CanManagePublicKeys returns true if this user is allowed to manage public keys
// from the WebClient. Used in WebClient UI
func (u *User) CanManagePublicKeys() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientPubKeyChangeDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientPubKeyChangeDisabled)
}
// CanManageTLSCerts returns true if this user is allowed to manage TLS certificates
// from the WebClient. Used in WebClient UI
func (u *User) CanManageTLSCerts() bool {
return !util.Contains(u.Filters.WebClient, sdk.WebClientTLSCertChangeDisabled)
return !slices.Contains(u.Filters.WebClient, sdk.WebClientTLSCertChangeDisabled)
}
// CanUpdateProfile returns true if the user is allowed to update the profile.
@ -1121,7 +1122,7 @@ func (u *User) CanUpdateProfile() bool {
// CanAddFilesFromWeb returns true if the client can add files from the web UI.
// The specified target is the directory where the files must be uploaded
func (u *User) CanAddFilesFromWeb(target string) bool {
if util.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
if slices.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
return false
}
return u.HasPerm(PermUpload, target) || u.HasPerm(PermOverwrite, target)
@ -1130,7 +1131,7 @@ func (u *User) CanAddFilesFromWeb(target string) bool {
// CanAddDirsFromWeb returns true if the client can add directories from the web UI.
// The specified target is the directory where the new directory must be created
func (u *User) CanAddDirsFromWeb(target string) bool {
if util.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
if slices.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
return false
}
return u.HasPerm(PermCreateDirs, target)
@ -1139,7 +1140,7 @@ func (u *User) CanAddDirsFromWeb(target string) bool {
// CanRenameFromWeb returns true if the client can rename objects from the web UI.
// The specified src and dest are the source and target directories for the rename.
func (u *User) CanRenameFromWeb(src, dest string) bool {
if util.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
if slices.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
return false
}
return u.HasAnyPerm(permsRenameAny, src) && u.HasAnyPerm(permsRenameAny, dest)
@ -1148,7 +1149,7 @@ func (u *User) CanRenameFromWeb(src, dest string) bool {
// CanDeleteFromWeb returns true if the client can delete objects from the web UI.
// The specified target is the parent directory for the object to delete
func (u *User) CanDeleteFromWeb(target string) bool {
if util.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
if slices.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
return false
}
return u.HasAnyPerm(permsDeleteAny, target)
@ -1157,7 +1158,7 @@ func (u *User) CanDeleteFromWeb(target string) bool {
// CanCopyFromWeb returns true if the client can copy objects from the web UI.
// The specified src and dest are the source and target directories for the copy.
func (u *User) CanCopyFromWeb(src, dest string) bool {
if util.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
if slices.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
return false
}
if !u.HasPerm(PermListItems, src) {
@ -1217,7 +1218,7 @@ func (u *User) MustSetSecondFactor() bool {
return true
}
for _, p := range u.Filters.TwoFactorAuthProtocols {
if !util.Contains(u.Filters.TOTPConfig.Protocols, p) {
if !slices.Contains(u.Filters.TOTPConfig.Protocols, p) {
return true
}
}
@ -1228,11 +1229,11 @@ func (u *User) MustSetSecondFactor() bool {
// MustSetSecondFactorForProtocol returns true if the user must set a second factor authentication
// for the specified protocol
func (u *User) MustSetSecondFactorForProtocol(protocol string) bool {
if util.Contains(u.Filters.TwoFactorAuthProtocols, protocol) {
if slices.Contains(u.Filters.TwoFactorAuthProtocols, protocol) {
if !u.Filters.TOTPConfig.Enabled {
return true
}
if !util.Contains(u.Filters.TOTPConfig.Protocols, protocol) {
if !slices.Contains(u.Filters.TOTPConfig.Protocols, protocol) {
return true
}
}

View file

@ -22,6 +22,7 @@ import (
"net"
"os"
"path/filepath"
"slices"
ftpserver "github.com/fclairamb/ftpserverlib"
"github.com/sftpgo/sdk/plugin/notifier"
@ -361,7 +362,7 @@ func (s *Server) validateUser(user dataprovider.User, cc ftpserver.ClientContext
user.Username, user.HomeDir)
return nil, fmt.Errorf("cannot login user with invalid home dir: %q", user.HomeDir)
}
if util.Contains(user.Filters.DeniedProtocols, common.ProtocolFTP) {
if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolFTP) {
logger.Info(logSender, connectionID, "cannot login user %q, protocol FTP is not allowed", user.Username)
return nil, fmt.Errorf("protocol FTP is not allowed for user %q", user.Username)
}

View file

@ -20,6 +20,7 @@ import (
"fmt"
"io"
"net/http"
"slices"
"strconv"
"strings"
@ -275,7 +276,7 @@ func saveUserTOTPConfig(username string, r *http.Request, recoveryCodes []datapr
return util.NewValidationError("two-factor authentication must be enabled")
}
for _, p := range userMerged.Filters.TwoFactorAuthProtocols {
if !util.Contains(user.Filters.TOTPConfig.Protocols, p) {
if !slices.Contains(user.Filters.TOTPConfig.Protocols, p) {
return util.NewValidationError(fmt.Sprintf("totp: the following protocols are required: %q",
strings.Join(userMerged.Filters.TwoFactorAuthProtocols, ", ")))
}

View file

@ -22,6 +22,7 @@ import (
"net/url"
"os"
"path"
"slices"
"strings"
"time"
@ -107,7 +108,7 @@ func addShare(w http.ResponseWriter, r *http.Request) {
share.Name = share.ShareID
}
if share.Password == "" {
if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
if slices.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
sendAPIResponse(w, r, nil, "You are not authorized to share files/folders without a password",
http.StatusForbidden)
return
@ -155,7 +156,7 @@ func updateShare(w http.ResponseWriter, r *http.Request) {
updatedShare.Password = share.Password
}
if updatedShare.Password == "" {
if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
if slices.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
sendAPIResponse(w, r, nil, "You are not authorized to share files/folders without a password",
http.StatusForbidden)
return
@ -434,7 +435,7 @@ func (s *httpdServer) getShareClaims(r *http.Request, shareID string) (*jwtToken
if tokenString == "" || invalidatedJWTTokens.Get(tokenString) {
return nil, errInvalidToken
}
if !util.Contains(token.Audience(), tokenAudienceWebShare) {
if !slices.Contains(token.Audience(), tokenAudienceWebShare) {
logger.Debug(logSender, "", "invalid token audience for share %q", shareID)
return nil, errInvalidToken
}
@ -486,7 +487,7 @@ func (s *httpdServer) checkPublicShare(w http.ResponseWriter, r *http.Request, v
renderError(err, "", statusCode)
return share, nil, err
}
if !util.Contains(validScopes, share.Scope) {
if !slices.Contains(validScopes, share.Scope) {
err := errors.New("invalid share scope")
renderError(util.NewI18nError(err, util.I18nErrorShareScope), "", http.StatusForbidden)
return share, nil, err
@ -543,7 +544,7 @@ func getUserForShare(share dataprovider.Share) (dataprovider.User, error) {
if !user.CanManageShares() {
return user, util.NewI18nError(util.NewRecordNotFoundError("this share does not exist"), util.I18nError404Message)
}
if share.Password == "" && util.Contains(user.Filters.WebClient, sdk.WebClientShareNoPasswordDisabled) {
if share.Password == "" && slices.Contains(user.Filters.WebClient, sdk.WebClientShareNoPasswordDisabled) {
return user, util.NewI18nError(
fmt.Errorf("sharing without a password was disabled: %w", os.ErrPermission),
util.I18nError403Message,

View file

@ -27,6 +27,7 @@ import (
"net/url"
"os"
"path"
"slices"
"strconv"
"strings"
"sync"
@ -727,7 +728,7 @@ func updateLoginMetrics(user *dataprovider.User, loginMethod, ip string, err err
}
func checkHTTPClientUser(user *dataprovider.User, r *http.Request, connectionID string, checkSessions bool) error {
if util.Contains(user.Filters.DeniedProtocols, common.ProtocolHTTP) {
if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolHTTP) {
logger.Info(logSender, connectionID, "cannot login user %q, protocol HTTP is not allowed", user.Username)
return util.NewI18nError(
fmt.Errorf("protocol HTTP is not allowed for user %q", user.Username),
@ -912,7 +913,7 @@ func isUserAllowedToResetPassword(r *http.Request, user *dataprovider.User) bool
if !user.CanResetPassword() {
return false
}
if util.Contains(user.Filters.DeniedProtocols, common.ProtocolHTTP) {
if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolHTTP) {
return false
}
if !user.IsLoginMethodAllowed(dataprovider.LoginMethodPassword, common.ProtocolHTTP) {

View file

@ -18,6 +18,7 @@ import (
"errors"
"fmt"
"net/http"
"slices"
"time"
"github.com/go-chi/jwtauth/v5"
@ -227,24 +228,24 @@ func (c *jwtTokenClaims) Decode(token map[string]any) {
}
func (c *jwtTokenClaims) isCriticalPermRemoved(permissions []string) bool {
if util.Contains(permissions, dataprovider.PermAdminAny) {
if slices.Contains(permissions, dataprovider.PermAdminAny) {
return false
}
if (util.Contains(c.Permissions, dataprovider.PermAdminManageAdmins) ||
util.Contains(c.Permissions, dataprovider.PermAdminAny)) &&
!util.Contains(permissions, dataprovider.PermAdminManageAdmins) &&
!util.Contains(permissions, dataprovider.PermAdminAny) {
if (slices.Contains(c.Permissions, dataprovider.PermAdminManageAdmins) ||
slices.Contains(c.Permissions, dataprovider.PermAdminAny)) &&
!slices.Contains(permissions, dataprovider.PermAdminManageAdmins) &&
!slices.Contains(permissions, dataprovider.PermAdminAny) {
return true
}
return false
}
func (c *jwtTokenClaims) hasPerm(perm string) bool {
if util.Contains(c.Permissions, dataprovider.PermAdminAny) {
if slices.Contains(c.Permissions, dataprovider.PermAdminAny) {
return true
}
return util.Contains(c.Permissions, perm)
return slices.Contains(c.Permissions, perm)
}
func (c *jwtTokenClaims) createToken(tokenAuth *jwtauth.JWTAuth, audience tokenAudience, ip string) (jwt.Token, string, error) {
@ -458,7 +459,7 @@ func verifyCSRFToken(r *http.Request, csrfTokenAuth *jwtauth.JWTAuth) error {
return fmt.Errorf("unable to verify form token: %v", err)
}
if !util.Contains(token.Audience(), tokenAudienceCSRF) {
if !slices.Contains(token.Audience(), tokenAudienceCSRF) {
logger.Debug(logSender, "", "error validating CSRF token audience")
return errors.New("the form token is not valid")
}
@ -495,7 +496,7 @@ func verifyLoginCookie(r *http.Request) error {
logger.Debug(logSender, "", "the login token has been invalidated")
return errInvalidToken
}
if !util.Contains(token.Audience(), tokenAudienceWebLogin) {
if !slices.Contains(token.Audience(), tokenAudienceWebLogin) {
logger.Debug(logSender, "", "the token with id %q is not valid for audience %q", token.JwtID(), tokenAudienceWebLogin)
return errInvalidToken
}
@ -543,7 +544,7 @@ func verifyOAuth2Token(csrfTokenAuth *jwtauth.JWTAuth, tokenString, ip string) (
)
}
if !util.Contains(token.Audience(), tokenAudienceOAuth2) {
if !slices.Contains(token.Audience(), tokenAudienceOAuth2) {
logger.Debug(logSender, "", "error validating OAuth2 token audience")
return "", util.NewI18nError(errors.New("invalid OAuth2 state"), util.I18nOAuth2InvalidState)
}
@ -563,7 +564,7 @@ func verifyOAuth2Token(csrfTokenAuth *jwtauth.JWTAuth, tokenString, ip string) (
func validateIPForToken(token jwt.Token, ip string) error {
if tokenValidationMode != tokenValidationNoIPMatch {
if !util.Contains(token.Audience(), ip) {
if !slices.Contains(token.Audience(), ip) {
return errInvalidToken
}
}

View file

@ -36,6 +36,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@ -1340,7 +1341,7 @@ func TestGroupSettingsOverride(t *testing.T) {
var folderNames []string
if assert.Len(t, user.VirtualFolders, 4) {
for _, f := range user.VirtualFolders {
if !util.Contains(folderNames, f.Name) {
if !slices.Contains(folderNames, f.Name) {
folderNames = append(folderNames, f.Name)
}
switch f.Name {
@ -1348,7 +1349,7 @@ func TestGroupSettingsOverride(t *testing.T) {
assert.Equal(t, mappedPath1, f.MappedPath)
assert.Equal(t, 3, f.BaseVirtualFolder.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 5, f.BaseVirtualFolder.FsConfig.OSConfig.WriteBufferSize)
assert.True(t, util.Contains([]string{"/vdir1", "/vdir2"}, f.VirtualPath))
assert.True(t, slices.Contains([]string{"/vdir1", "/vdir2"}, f.VirtualPath))
case folderName2:
assert.Equal(t, mappedPath2, f.MappedPath)
assert.Equal(t, "/vdir3", f.VirtualPath)
@ -2103,16 +2104,16 @@ func TestActionRuleRelations(t *testing.T) {
action1, _, err = httpdtest.GetEventActionByName(action1.Name, http.StatusOK)
assert.NoError(t, err)
assert.Len(t, action1.Rules, 1)
assert.True(t, util.Contains(action1.Rules, rule1.Name))
assert.True(t, slices.Contains(action1.Rules, rule1.Name))
action2, _, err = httpdtest.GetEventActionByName(action2.Name, http.StatusOK)
assert.NoError(t, err)
assert.Len(t, action2.Rules, 1)
assert.True(t, util.Contains(action2.Rules, rule2.Name))
assert.True(t, slices.Contains(action2.Rules, rule2.Name))
action3, _, err = httpdtest.GetEventActionByName(action3.Name, http.StatusOK)
assert.NoError(t, err)
assert.Len(t, action3.Rules, 2)
assert.True(t, util.Contains(action3.Rules, rule1.Name))
assert.True(t, util.Contains(action3.Rules, rule2.Name))
assert.True(t, slices.Contains(action3.Rules, rule1.Name))
assert.True(t, slices.Contains(action3.Rules, rule2.Name))
// referenced actions cannot be removed
_, err = httpdtest.RemoveEventAction(action1, http.StatusBadRequest)
assert.NoError(t, err)
@ -2140,7 +2141,7 @@ func TestActionRuleRelations(t *testing.T) {
action3, _, err = httpdtest.GetEventActionByName(action3.Name, http.StatusOK)
assert.NoError(t, err)
assert.Len(t, action3.Rules, 1)
assert.True(t, util.Contains(action3.Rules, rule1.Name))
assert.True(t, slices.Contains(action3.Rules, rule1.Name))
_, err = httpdtest.RemoveEventRule(rule1, http.StatusOK)
assert.NoError(t, err)
@ -8912,7 +8913,7 @@ func TestBasicUserHandlingMock(t *testing.T) {
assert.Equal(t, user.MaxSessions, updatedUser.MaxSessions)
assert.Equal(t, user.UploadBandwidth, updatedUser.UploadBandwidth)
assert.Equal(t, 1, len(updatedUser.Permissions["/"]))
assert.True(t, util.Contains(updatedUser.Permissions["/"], dataprovider.PermAny))
assert.True(t, slices.Contains(updatedUser.Permissions["/"], dataprovider.PermAny))
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+user.Username, nil)
setBearerForReq(req, token)
rr = executeRequest(req)
@ -12140,7 +12141,7 @@ func TestUserPermissionsMock(t *testing.T) {
err = render.DecodeJSON(rr.Body, &updatedUser)
assert.NoError(t, err)
if val, ok := updatedUser.Permissions["/otherdir"]; ok {
assert.True(t, util.Contains(val, dataprovider.PermListItems))
assert.True(t, slices.Contains(val, dataprovider.PermListItems))
assert.Equal(t, 1, len(val))
} else {
assert.Fail(t, "expected dir not found in permissions")
@ -21552,10 +21553,10 @@ func TestWebUserAddMock(t *testing.T) {
assert.Equal(t, 60, newUser.Filters.PasswordStrength)
assert.Greater(t, newUser.LastPasswordChange, int64(0))
assert.True(t, newUser.Filters.RequirePasswordChange)
assert.True(t, util.Contains(newUser.PublicKeys, testPubKey))
assert.True(t, slices.Contains(newUser.PublicKeys, testPubKey))
if val, ok := newUser.Permissions["/subdir"]; ok {
assert.True(t, util.Contains(val, dataprovider.PermListItems))
assert.True(t, util.Contains(val, dataprovider.PermDownload))
assert.True(t, slices.Contains(val, dataprovider.PermListItems))
assert.True(t, slices.Contains(val, dataprovider.PermDownload))
} else {
assert.Fail(t, "user permissions must contain /somedir", "actual: %v", newUser.Permissions)
}
@ -21574,20 +21575,20 @@ func TestWebUserAddMock(t *testing.T) {
case "/dir1":
assert.Len(t, filter.DeniedPatterns, 1)
assert.Len(t, filter.AllowedPatterns, 1)
assert.True(t, util.Contains(filter.AllowedPatterns, "*.png"))
assert.True(t, util.Contains(filter.DeniedPatterns, "*.zip"))
assert.True(t, slices.Contains(filter.AllowedPatterns, "*.png"))
assert.True(t, slices.Contains(filter.DeniedPatterns, "*.zip"))
assert.Equal(t, sdk.DenyPolicyDefault, filter.DenyPolicy)
case "/dir2":
assert.Len(t, filter.DeniedPatterns, 1)
assert.Len(t, filter.AllowedPatterns, 2)
assert.True(t, util.Contains(filter.AllowedPatterns, "*.jpg"))
assert.True(t, util.Contains(filter.AllowedPatterns, "*.png"))
assert.True(t, util.Contains(filter.DeniedPatterns, "*.mkv"))
assert.True(t, slices.Contains(filter.AllowedPatterns, "*.jpg"))
assert.True(t, slices.Contains(filter.AllowedPatterns, "*.png"))
assert.True(t, slices.Contains(filter.DeniedPatterns, "*.mkv"))
assert.Equal(t, sdk.DenyPolicyHide, filter.DenyPolicy)
case "/dir3":
assert.Len(t, filter.DeniedPatterns, 1)
assert.Len(t, filter.AllowedPatterns, 0)
assert.True(t, util.Contains(filter.DeniedPatterns, "*.rar"))
assert.True(t, slices.Contains(filter.DeniedPatterns, "*.rar"))
assert.Equal(t, sdk.DenyPolicyDefault, filter.DenyPolicy)
}
}
@ -21828,16 +21829,16 @@ func TestWebUserUpdateMock(t *testing.T) {
assert.Equal(t, 40, updateUser.Filters.PasswordStrength)
assert.True(t, updateUser.Filters.RequirePasswordChange)
if val, ok := updateUser.Permissions["/otherdir"]; ok {
assert.True(t, util.Contains(val, dataprovider.PermListItems))
assert.True(t, util.Contains(val, dataprovider.PermUpload))
assert.True(t, slices.Contains(val, dataprovider.PermListItems))
assert.True(t, slices.Contains(val, dataprovider.PermUpload))
} else {
assert.Fail(t, "user permissions must contains /otherdir", "actual: %v", updateUser.Permissions)
}
assert.True(t, util.Contains(updateUser.Filters.AllowedIP, "192.168.1.3/32"))
assert.True(t, util.Contains(updateUser.Filters.DeniedIP, "10.0.0.2/32"))
assert.True(t, util.Contains(updateUser.Filters.DeniedLoginMethods, dataprovider.SSHLoginMethodKeyboardInteractive))
assert.True(t, util.Contains(updateUser.Filters.DeniedProtocols, common.ProtocolFTP))
assert.True(t, util.Contains(updateUser.Filters.FilePatterns[0].DeniedPatterns, "*.zip"))
assert.True(t, slices.Contains(updateUser.Filters.AllowedIP, "192.168.1.3/32"))
assert.True(t, slices.Contains(updateUser.Filters.DeniedIP, "10.0.0.2/32"))
assert.True(t, slices.Contains(updateUser.Filters.DeniedLoginMethods, dataprovider.SSHLoginMethodKeyboardInteractive))
assert.True(t, slices.Contains(updateUser.Filters.DeniedProtocols, common.ProtocolFTP))
assert.True(t, slices.Contains(updateUser.Filters.FilePatterns[0].DeniedPatterns, "*.zip"))
assert.Len(t, updateUser.Filters.BandwidthLimits, 0)
assert.Len(t, updateUser.Filters.TLSCerts, 1)
req, err = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)

View file

@ -20,6 +20,7 @@ import (
"io/fs"
"net/http"
"net/url"
"slices"
"strings"
"github.com/go-chi/jwtauth/v5"
@ -83,7 +84,7 @@ func validateJWTToken(w http.ResponseWriter, r *http.Request, audience tokenAudi
if err := checkPartialAuth(w, r, audience, token.Audience()); err != nil {
return err
}
if !util.Contains(token.Audience(), audience) {
if !slices.Contains(token.Audience(), audience) {
logger.Debug(logSender, "", "the token is not valid for audience %q", audience)
doRedirect("Your token audience is not valid", nil)
return errInvalidToken
@ -113,7 +114,7 @@ func (s *httpdServer) validateJWTPartialToken(w http.ResponseWriter, r *http.Req
notFoundFunc(w, r, nil)
return errInvalidToken
}
if !util.Contains(token.Audience(), audience) {
if !slices.Contains(token.Audience(), audience) {
logger.Debug(logSender, "", "the partial token with id %q is not valid for audience %q", token.JwtID(), audience)
notFoundFunc(w, r, nil)
return errInvalidToken
@ -331,7 +332,7 @@ func (s *httpdServer) verifyCSRFHeader(next http.Handler) http.Handler {
return
}
if !util.Contains(token.Audience(), tokenAudienceCSRF) {
if !slices.Contains(token.Audience(), tokenAudienceCSRF) {
logger.Debug(logSender, "", "error validating CSRF header token audience")
sendAPIResponse(w, r, errors.New("the token is not valid"), "", http.StatusForbidden)
return
@ -571,11 +572,11 @@ func authenticateUserWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTAu
}
func checkPartialAuth(w http.ResponseWriter, r *http.Request, audience string, tokenAudience []string) error {
if audience == tokenAudienceWebAdmin && util.Contains(tokenAudience, tokenAudienceWebAdminPartial) {
if audience == tokenAudienceWebAdmin && slices.Contains(tokenAudience, tokenAudienceWebAdminPartial) {
http.Redirect(w, r, webAdminTwoFactorPath, http.StatusFound)
return errInvalidToken
}
if audience == tokenAudienceWebClient && util.Contains(tokenAudience, tokenAudienceWebClientPartial) {
if audience == tokenAudienceWebClient && slices.Contains(tokenAudience, tokenAudienceWebClientPartial) {
http.Redirect(w, r, webClientTwoFactorPath, http.StatusFound)
return errInvalidToken
}

View file

@ -21,6 +21,7 @@ import (
"fmt"
"net/http"
"net/url"
"slices"
"strings"
"time"
@ -143,7 +144,7 @@ func (o *OIDC) initialize() error {
if o.RedirectBaseURL == "" {
return errors.New("oidc: redirect base URL cannot be empty")
}
if !util.Contains(o.Scopes, oidc.ScopeOpenID) {
if !slices.Contains(o.Scopes, oidc.ScopeOpenID) {
return fmt.Errorf("oidc: required scope %q is not set", oidc.ScopeOpenID)
}
if o.ClientSecretFile != "" {

View file

@ -26,6 +26,7 @@ import (
"net/url"
"path"
"path/filepath"
"slices"
"strings"
"time"
@ -355,7 +356,7 @@ func (s *httpdServer) handleWebClientTwoFactorRecoveryPost(w http.ResponseWriter
util.NewI18nError(dataprovider.ErrInvalidCredentials, util.I18nErrorInvalidCredentials))
return
}
if !userMerged.Filters.TOTPConfig.Enabled || !util.Contains(userMerged.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) {
if !userMerged.Filters.TOTPConfig.Enabled || !slices.Contains(userMerged.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) {
s.renderClientTwoFactorPage(w, r, util.NewI18nError(
util.NewValidationError("two factory authentication is not enabled"), util.I18n2FADisabled))
return
@ -423,7 +424,7 @@ func (s *httpdServer) handleWebClientTwoFactorPost(w http.ResponseWriter, r *htt
s.renderClientTwoFactorPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCredentials))
return
}
if !user.Filters.TOTPConfig.Enabled || !util.Contains(user.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) {
if !user.Filters.TOTPConfig.Enabled || !slices.Contains(user.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, common.ErrInternalFailure)
s.renderClientTwoFactorPage(w, r, util.NewI18nError(common.ErrInternalFailure, util.I18n2FADisabled))
return
@ -743,7 +744,7 @@ func (s *httpdServer) loginUser(
}
audience := tokenAudienceWebClient
if user.Filters.TOTPConfig.Enabled && util.Contains(user.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) &&
if user.Filters.TOTPConfig.Enabled && slices.Contains(user.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) &&
user.CanManageMFA() && !isSecondFactorAuth {
audience = tokenAudienceWebClientPartial
}
@ -863,7 +864,7 @@ func (s *httpdServer) getUserToken(w http.ResponseWriter, r *http.Request) {
return
}
if user.Filters.TOTPConfig.Enabled && util.Contains(user.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) {
if user.Filters.TOTPConfig.Enabled && slices.Contains(user.Filters.TOTPConfig.Protocols, common.ProtocolHTTP) {
passcode := r.Header.Get(otpHeaderCode)
if passcode == "" {
logger.Debug(logSender, "", "TOTP enabled for user %q and not passcode provided, authentication refused", user.Username)
@ -1009,7 +1010,7 @@ func (s *httpdServer) checkCookieExpiration(w http.ResponseWriter, r *http.Reque
if time.Until(token.Expiration()) > tokenRefreshThreshold {
return
}
if util.Contains(token.Audience(), tokenAudienceWebClient) {
if slices.Contains(token.Audience(), tokenAudienceWebClient) {
s.refreshClientToken(w, r, &tokenClaims)
} else {
s.refreshAdminToken(w, r, &tokenClaims)

View file

@ -25,6 +25,7 @@ import (
"net/url"
"os"
"path/filepath"
"slices"
"sort"
"strconv"
"strings"
@ -1488,13 +1489,13 @@ func getFiltersFromUserPostFields(r *http.Request) (sdk.BaseUserFilters, error)
filters.PasswordStrength = passwordStrength
filters.AccessTime = getAccessTimeRestrictionsFromPostFields(r)
hooks := r.Form["hooks"]
if util.Contains(hooks, "external_auth_disabled") {
if slices.Contains(hooks, "external_auth_disabled") {
filters.Hooks.ExternalAuthDisabled = true
}
if util.Contains(hooks, "pre_login_disabled") {
if slices.Contains(hooks, "pre_login_disabled") {
filters.Hooks.PreLoginDisabled = true
}
if util.Contains(hooks, "check_password_disabled") {
if slices.Contains(hooks, "check_password_disabled") {
filters.Hooks.CheckPasswordDisabled = true
}
filters.IsAnonymous = r.Form.Get("is_anonymous") != ""
@ -2215,7 +2216,7 @@ func getFoldersRetentionFromPostFields(r *http.Request) ([]dataprovider.FolderRe
res = append(res, dataprovider.FolderRetention{
Path: p,
Retention: retention,
DeleteEmptyDirs: util.Contains(opts, "1"),
DeleteEmptyDirs: slices.Contains(opts, "1"),
})
}
}
@ -2557,9 +2558,9 @@ func getEventRuleActionsFromPostFields(r *http.Request) []dataprovider.EventActi
},
Order: order + 1,
Options: dataprovider.EventActionOptions{
IsFailureAction: util.Contains(options, "1"),
StopOnFailure: util.Contains(options, "2"),
ExecuteSync: util.Contains(options, "3"),
IsFailureAction: slices.Contains(options, "1"),
StopOnFailure: slices.Contains(options, "2"),
ExecuteSync: slices.Contains(options, "3"),
},
})
}

View file

@ -27,6 +27,7 @@ import (
"os"
"path"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
@ -1463,7 +1464,7 @@ func (s *httpdServer) handleClientAddSharePost(w http.ResponseWriter, r *http.Re
share.LastUseAt = 0
share.Username = claims.Username
if share.Password == "" {
if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
if slices.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
s.renderAddUpdateSharePage(w, r, share,
util.NewI18nError(util.NewValidationError("You are not allowed to share files/folders without password"), util.I18nErrorShareNoPwd),
true)
@ -1532,7 +1533,7 @@ func (s *httpdServer) handleClientUpdateSharePost(w http.ResponseWriter, r *http
updatedShare.Password = share.Password
}
if updatedShare.Password == "" {
if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
if slices.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
s.renderAddUpdateSharePage(w, r, updatedShare,
util.NewI18nError(util.NewValidationError("You are not allowed to share files/folders without password"), util.I18nErrorShareNoPwd),
false)
@ -2015,7 +2016,7 @@ func doCheckExist(w http.ResponseWriter, r *http.Request, connection *Connection
}
existing := make([]map[string]any, 0)
for _, info := range contents {
if util.Contains(filesList.Files, info.Name()) {
if slices.Contains(filesList.Files, info.Name()) {
res := make(map[string]any)
res["name"] = info.Name()
if info.IsDir() {

View file

@ -25,6 +25,7 @@ import (
"net/http"
"net/url"
"path"
"slices"
"strconv"
"strings"
@ -36,7 +37,6 @@ import (
"github.com/drakkan/sftpgo/v2/internal/httpclient"
"github.com/drakkan/sftpgo/v2/internal/httpd"
"github.com/drakkan/sftpgo/v2/internal/kms"
"github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
"github.com/drakkan/sftpgo/v2/internal/vfs"
)
@ -1679,7 +1679,7 @@ func checkEventConditionOptions(expected, actual dataprovider.ConditionOptions)
return errors.New("condition protocols mismatch")
}
for _, v := range expected.Protocols {
if !util.Contains(actual.Protocols, v) {
if !slices.Contains(actual.Protocols, v) {
return errors.New("condition protocols content mismatch")
}
}
@ -1687,7 +1687,7 @@ func checkEventConditionOptions(expected, actual dataprovider.ConditionOptions)
return errors.New("condition provider objects mismatch")
}
for _, v := range expected.ProviderObjects {
if !util.Contains(actual.ProviderObjects, v) {
if !slices.Contains(actual.ProviderObjects, v) {
return errors.New("condition provider objects content mismatch")
}
}
@ -1705,7 +1705,7 @@ func checkEventConditions(expected, actual dataprovider.EventConditions) error {
return errors.New("fs events mismatch")
}
for _, v := range expected.FsEvents {
if !util.Contains(actual.FsEvents, v) {
if !slices.Contains(actual.FsEvents, v) {
return errors.New("fs events content mismatch")
}
}
@ -1713,7 +1713,7 @@ func checkEventConditions(expected, actual dataprovider.EventConditions) error {
return errors.New("provider events mismatch")
}
for _, v := range expected.ProviderEvents {
if !util.Contains(actual.ProviderEvents, v) {
if !slices.Contains(actual.ProviderEvents, v) {
return errors.New("provider events content mismatch")
}
}
@ -1948,7 +1948,7 @@ func checkAdmin(expected, actual *dataprovider.Admin) error {
return errors.New("permissions mismatch")
}
for _, p := range expected.Permissions {
if !util.Contains(actual.Permissions, p) {
if !slices.Contains(actual.Permissions, p) {
return errors.New("permissions content mismatch")
}
}
@ -1966,7 +1966,7 @@ func compareAdminFilters(expected, actual dataprovider.AdminFilters) error {
return errors.New("allow list mismatch")
}
for _, v := range expected.AllowList {
if !util.Contains(actual.AllowList, v) {
if !slices.Contains(actual.AllowList, v) {
return errors.New("allow list content mismatch")
}
}
@ -2057,7 +2057,7 @@ func compareUserPermissions(expected map[string][]string, actual map[string][]st
for dir, perms := range expected {
if actualPerms, ok := actual[dir]; ok {
for _, v := range actualPerms {
if !util.Contains(perms, v) {
if !slices.Contains(perms, v) {
return errors.New("permissions contents mismatch")
}
}
@ -2310,7 +2310,7 @@ func compareSFTPFsConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error
return errors.New("SFTPFs fingerprints mismatch")
}
for _, value := range actual.SFTPConfig.Fingerprints {
if !util.Contains(expected.SFTPConfig.Fingerprints, value) {
if !slices.Contains(expected.SFTPConfig.Fingerprints, value) {
return errors.New("SFTPFs fingerprints mismatch")
}
}
@ -2401,27 +2401,27 @@ func checkEncryptedSecret(expected, actual *kms.Secret) error {
func compareUserFilterSubStructs(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
for _, IPMask := range expected.AllowedIP {
if !util.Contains(actual.AllowedIP, IPMask) {
if !slices.Contains(actual.AllowedIP, IPMask) {
return errors.New("allowed IP contents mismatch")
}
}
for _, IPMask := range expected.DeniedIP {
if !util.Contains(actual.DeniedIP, IPMask) {
if !slices.Contains(actual.DeniedIP, IPMask) {
return errors.New("denied IP contents mismatch")
}
}
for _, method := range expected.DeniedLoginMethods {
if !util.Contains(actual.DeniedLoginMethods, method) {
if !slices.Contains(actual.DeniedLoginMethods, method) {
return errors.New("denied login methods contents mismatch")
}
}
for _, protocol := range expected.DeniedProtocols {
if !util.Contains(actual.DeniedProtocols, protocol) {
if !slices.Contains(actual.DeniedProtocols, protocol) {
return errors.New("denied protocols contents mismatch")
}
}
for _, options := range expected.WebClient {
if !util.Contains(actual.WebClient, options) {
if !slices.Contains(actual.WebClient, options) {
return errors.New("web client options contents mismatch")
}
}
@ -2430,7 +2430,7 @@ func compareUserFilterSubStructs(expected sdk.BaseUserFilters, actual sdk.BaseUs
return errors.New("TLS certs mismatch")
}
for _, cert := range expected.TLSCerts {
if !util.Contains(actual.TLSCerts, cert) {
if !slices.Contains(actual.TLSCerts, cert) {
return errors.New("TLS certs content mismatch")
}
}
@ -2527,7 +2527,7 @@ func checkFilterMatch(expected []string, actual []string) bool {
return false
}
for _, e := range expected {
if !util.Contains(actual, strings.ToLower(e)) {
if !slices.Contains(actual, strings.ToLower(e)) {
return false
}
}
@ -2570,7 +2570,7 @@ func compareUserBandwidthLimitFilters(expected sdk.BaseUserFilters, actual sdk.B
return errors.New("bandwidth filters sources mismatch")
}
for _, source := range actual.BandwidthLimits[idx].Sources {
if !util.Contains(l.Sources, source) {
if !slices.Contains(l.Sources, source) {
return errors.New("bandwidth filters source mismatch")
}
}
@ -2680,7 +2680,7 @@ func compareEventActionEmailConfigFields(expected, actual dataprovider.EventActi
return errors.New("email recipients mismatch")
}
for _, v := range expected.Recipients {
if !util.Contains(actual.Recipients, v) {
if !slices.Contains(actual.Recipients, v) {
return errors.New("email recipients content mismatch")
}
}
@ -2688,7 +2688,7 @@ func compareEventActionEmailConfigFields(expected, actual dataprovider.EventActi
return errors.New("email bcc mismatch")
}
for _, v := range expected.Bcc {
if !util.Contains(actual.Bcc, v) {
if !slices.Contains(actual.Bcc, v) {
return errors.New("email bcc content mismatch")
}
}
@ -2705,7 +2705,7 @@ func compareEventActionEmailConfigFields(expected, actual dataprovider.EventActi
return errors.New("email attachments mismatch")
}
for _, v := range expected.Attachments {
if !util.Contains(actual.Attachments, v) {
if !slices.Contains(actual.Attachments, v) {
return errors.New("email attachments content mismatch")
}
}
@ -2720,7 +2720,7 @@ func compareEventActionFsCompressFields(expected, actual dataprovider.EventActio
return errors.New("fs compress paths mismatch")
}
for _, v := range expected.Paths {
if !util.Contains(actual.Paths, v) {
if !slices.Contains(actual.Paths, v) {
return errors.New("fs compress paths content mismatch")
}
}
@ -2741,7 +2741,7 @@ func compareEventActionFsConfigFields(expected, actual dataprovider.EventActionF
return errors.New("fs deletes mismatch")
}
for _, v := range expected.Deletes {
if !util.Contains(actual.Deletes, v) {
if !slices.Contains(actual.Deletes, v) {
return errors.New("fs deletes content mismatch")
}
}
@ -2749,7 +2749,7 @@ func compareEventActionFsConfigFields(expected, actual dataprovider.EventActionF
return errors.New("fs mkdirs mismatch")
}
for _, v := range expected.MkDirs {
if !util.Contains(actual.MkDirs, v) {
if !slices.Contains(actual.MkDirs, v) {
return errors.New("fs mkdir content mismatch")
}
}
@ -2757,7 +2757,7 @@ func compareEventActionFsConfigFields(expected, actual dataprovider.EventActionF
return errors.New("fs exist mismatch")
}
for _, v := range expected.Exist {
if !util.Contains(actual.Exist, v) {
if !slices.Contains(actual.Exist, v) {
return errors.New("fs exist content mismatch")
}
}
@ -2788,7 +2788,7 @@ func compareEventActionCmdConfigFields(expected, actual dataprovider.EventAction
return errors.New("cmd args mismatch")
}
for _, v := range expected.Args {
if !util.Contains(actual.Args, v) {
if !slices.Contains(actual.Args, v) {
return errors.New("cmd args content mismatch")
}
}

View file

@ -17,6 +17,7 @@ package plugin
import (
"fmt"
"path/filepath"
"slices"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
@ -25,7 +26,6 @@ import (
"github.com/drakkan/sftpgo/v2/internal/kms"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util"
)
var (
@ -41,10 +41,10 @@ type KMSConfig struct {
}
func (c *KMSConfig) validate() error {
if !util.Contains(validKMSSchemes, c.Scheme) {
if !slices.Contains(validKMSSchemes, c.Scheme) {
return fmt.Errorf("invalid kms scheme: %v", c.Scheme)
}
if !util.Contains(validKMSEncryptedStatuses, c.EncryptedStatus) {
if !slices.Contains(validKMSEncryptedStatuses, c.EncryptedStatus) {
return fmt.Errorf("invalid kms encrypted status: %v", c.EncryptedStatus)
}
return nil

View file

@ -16,6 +16,7 @@ package plugin
import (
"fmt"
"slices"
"sync"
"time"
@ -24,7 +25,6 @@ import (
"github.com/sftpgo/sdk/plugin/notifier"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util"
)
// NotifierConfig defines configuration parameters for notifiers plugins
@ -220,7 +220,7 @@ func (p *notifierPlugin) canQueueEvent(timestamp int64) bool {
}
func (p *notifierPlugin) notifyFsAction(event *notifier.FsEvent) {
if !util.Contains(p.config.NotifierOptions.FsEvents, event.Action) {
if !slices.Contains(p.config.NotifierOptions.FsEvents, event.Action) {
return
}
@ -233,8 +233,8 @@ func (p *notifierPlugin) notifyFsAction(event *notifier.FsEvent) {
}
func (p *notifierPlugin) notifyProviderAction(event *notifier.ProviderEvent, object Renderer) {
if !util.Contains(p.config.NotifierOptions.ProviderEvents, event.Action) ||
!util.Contains(p.config.NotifierOptions.ProviderObjects, event.ObjectType) {
if !slices.Contains(p.config.NotifierOptions.ProviderEvents, event.Action) ||
!slices.Contains(p.config.NotifierOptions.ProviderObjects, event.ObjectType) {
return
}

View file

@ -24,6 +24,7 @@ import (
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
"sync"
"sync/atomic"
@ -336,7 +337,7 @@ func (m *Manager) NotifyLogEvent(event notifier.LogEventType, protocol, username
var e *notifier.LogEvent
for _, n := range m.notifiers {
if util.Contains(n.config.NotifierOptions.LogEvents, int(event)) {
if slices.Contains(n.config.NotifierOptions.LogEvents, int(event)) {
if e == nil {
message := ""
if err != nil {

View file

@ -20,6 +20,7 @@ package service
import (
"fmt"
"math/rand"
"slices"
"strings"
"github.com/sftpgo/sdk"
@ -211,7 +212,7 @@ func configurePortableSFTPService(port int, enabledSSHCommands []string) {
} else {
sftpdConf.Bindings[0].Port = 0
}
if util.Contains(enabledSSHCommands, "*") {
if slices.Contains(enabledSSHCommands, "*") {
sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
} else {
sftpdConf.EnabledSSHCommands = enabledSSHCommands

View file

@ -24,6 +24,7 @@ import (
"os"
"path/filepath"
"runtime"
"slices"
"testing"
"time"
@ -418,7 +419,7 @@ func TestSupportedSSHCommands(t *testing.T) {
assert.Equal(t, len(supportedSSHCommands), len(cmds))
for _, c := range cmds {
assert.True(t, util.Contains(supportedSSHCommands, c))
assert.True(t, slices.Contains(supportedSSHCommands, c))
}
}
@ -842,7 +843,7 @@ func TestRsyncOptions(t *testing.T) {
}
cmd, err := sshCmd.getSystemCommand()
assert.NoError(t, err)
assert.True(t, util.Contains(cmd.cmd.Args, "--safe-links"),
assert.True(t, slices.Contains(cmd.cmd.Args, "--safe-links"),
"--safe-links must be added if the user has the create symlinks permission")
permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
@ -859,7 +860,7 @@ func TestRsyncOptions(t *testing.T) {
}
cmd, err = sshCmd.getSystemCommand()
assert.NoError(t, err)
assert.True(t, util.Contains(cmd.cmd.Args, "--munge-links"),
assert.True(t, slices.Contains(cmd.cmd.Args, "--munge-links"),
"--munge-links must be added if the user has the create symlinks permission")
sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{

View file

@ -26,6 +26,7 @@ import (
"os"
"path/filepath"
"runtime/debug"
"slices"
"strings"
"sync"
"time"
@ -263,13 +264,13 @@ func (c *Configuration) getServerConfig() *ssh.ServerConfig {
func (c *Configuration) updateSupportedAuthentications() {
serviceStatus.Authentications = util.RemoveDuplicates(serviceStatus.Authentications, false)
if util.Contains(serviceStatus.Authentications, dataprovider.LoginMethodPassword) &&
util.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) {
if slices.Contains(serviceStatus.Authentications, dataprovider.LoginMethodPassword) &&
slices.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) {
serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyAndPassword)
}
if util.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyboardInteractive) &&
util.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) {
if slices.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyboardInteractive) &&
slices.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) {
serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyAndKeyboardInt)
}
}
@ -422,7 +423,7 @@ func (c *Configuration) configureKeyAlgos(serverConfig *ssh.ServerConfig) error
c.HostKeyAlgorithms = util.RemoveDuplicates(c.HostKeyAlgorithms, true)
}
for _, hostKeyAlgo := range c.HostKeyAlgorithms {
if !util.Contains(supportedHostKeyAlgos, hostKeyAlgo) {
if !slices.Contains(supportedHostKeyAlgos, hostKeyAlgo) {
return fmt.Errorf("unsupported host key algorithm %q", hostKeyAlgo)
}
}
@ -430,7 +431,7 @@ func (c *Configuration) configureKeyAlgos(serverConfig *ssh.ServerConfig) error
if len(c.PublicKeyAlgorithms) > 0 {
c.PublicKeyAlgorithms = util.RemoveDuplicates(c.PublicKeyAlgorithms, true)
for _, algo := range c.PublicKeyAlgorithms {
if !util.Contains(supportedPublicKeyAlgos, algo) {
if !slices.Contains(supportedPublicKeyAlgos, algo) {
return fmt.Errorf("unsupported public key authentication algorithm %q", algo)
}
}
@ -472,7 +473,7 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
if kex == keyExchangeCurve25519SHA256LibSSH {
continue
}
if !util.Contains(supportedKexAlgos, kex) {
if !slices.Contains(supportedKexAlgos, kex) {
return fmt.Errorf("unsupported key-exchange algorithm %q", kex)
}
}
@ -486,7 +487,7 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
if len(c.Ciphers) > 0 {
c.Ciphers = util.RemoveDuplicates(c.Ciphers, true)
for _, cipher := range c.Ciphers {
if !util.Contains(supportedCiphers, cipher) {
if !slices.Contains(supportedCiphers, cipher) {
return fmt.Errorf("unsupported cipher %q", cipher)
}
}
@ -499,7 +500,7 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
if len(c.MACs) > 0 {
c.MACs = util.RemoveDuplicates(c.MACs, true)
for _, mac := range c.MACs {
if !util.Contains(supportedMACs, mac) {
if !slices.Contains(supportedMACs, mac) {
return fmt.Errorf("unsupported MAC algorithm %q", mac)
}
}
@ -785,7 +786,7 @@ func loginUser(user *dataprovider.User, loginMethod, publicKey string, conn ssh.
user.Username, user.HomeDir)
return nil, fmt.Errorf("cannot login user with invalid home dir: %q", user.HomeDir)
}
if util.Contains(user.Filters.DeniedProtocols, common.ProtocolSSH) {
if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolSSH) {
logger.Info(logSender, connectionID, "cannot login user %q, protocol SSH is not allowed", user.Username)
return nil, fmt.Errorf("protocol SSH is not allowed for user %q", user.Username)
}
@ -830,14 +831,14 @@ func loginUser(user *dataprovider.User, loginMethod, publicKey string, conn ssh.
}
func (c *Configuration) checkSSHCommands() {
if util.Contains(c.EnabledSSHCommands, "*") {
if slices.Contains(c.EnabledSSHCommands, "*") {
c.EnabledSSHCommands = GetSupportedSSHCommands()
return
}
sshCommands := []string{}
for _, command := range c.EnabledSSHCommands {
command = strings.TrimSpace(command)
if util.Contains(supportedSSHCommands, command) {
if slices.Contains(supportedSSHCommands, command) {
sshCommands = append(sshCommands, command)
} else {
logger.Warn(logSender, "", "unsupported ssh command: %q ignored", command)
@ -927,7 +928,7 @@ func (c *Configuration) checkHostKeyAutoGeneration(configDir string) error {
func (c *Configuration) getHostKeyAlgorithms(keyFormat string) []string {
var algos []string
for _, algo := range algorithmsForKeyFormat(keyFormat) {
if util.Contains(c.HostKeyAlgorithms, algo) {
if slices.Contains(c.HostKeyAlgorithms, algo) {
algos = append(algos, algo)
}
}
@ -986,7 +987,7 @@ func (c *Configuration) checkAndLoadHostKeys(configDir string, serverConfig *ssh
var algos []string
for _, algo := range algorithmsForKeyFormat(signer.PublicKey().Type()) {
if underlyingAlgo, ok := certKeyAlgoNames[algo]; ok {
if util.Contains(mas.Algorithms(), underlyingAlgo) {
if slices.Contains(mas.Algorithms(), underlyingAlgo) {
algos = append(algos, algo)
}
}
@ -1098,12 +1099,12 @@ func (c *Configuration) initializeCertChecker(configDir string) error {
func (c *Configuration) getPartialSuccessError(nextAuthMethods []string) error {
err := &ssh.PartialSuccessError{}
if c.PasswordAuthentication && util.Contains(nextAuthMethods, dataprovider.LoginMethodPassword) {
if c.PasswordAuthentication && slices.Contains(nextAuthMethods, dataprovider.LoginMethodPassword) {
err.Next.PasswordCallback = func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
return c.validatePasswordCredentials(conn, password, dataprovider.SSHLoginMethodKeyAndPassword)
}
}
if c.KeyboardInteractiveAuthentication && util.Contains(nextAuthMethods, dataprovider.SSHLoginMethodKeyboardInteractive) {
if c.KeyboardInteractiveAuthentication && slices.Contains(nextAuthMethods, dataprovider.SSHLoginMethodKeyboardInteractive) {
err.Next.KeyboardInteractiveCallback = func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
return c.validateKeyboardInteractiveCredentials(conn, client, dataprovider.SSHLoginMethodKeyAndKeyboardInt, true)
}

View file

@ -38,6 +38,7 @@ import (
"path"
"path/filepath"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@ -8639,8 +8640,8 @@ func TestUserAllowedLoginMethods(t *testing.T) {
allowedMethods = user.GetAllowedLoginMethods()
assert.Equal(t, 4, len(allowedMethods))
assert.True(t, util.Contains(allowedMethods, dataprovider.SSHLoginMethodKeyAndKeyboardInt))
assert.True(t, util.Contains(allowedMethods, dataprovider.SSHLoginMethodKeyAndPassword))
assert.True(t, slices.Contains(allowedMethods, dataprovider.SSHLoginMethodKeyAndKeyboardInt))
assert.True(t, slices.Contains(allowedMethods, dataprovider.SSHLoginMethodKeyAndPassword))
}
func TestUserPartialAuth(t *testing.T) {

View file

@ -27,6 +27,7 @@ import (
"os/exec"
"path"
"runtime/debug"
"slices"
"strings"
"sync"
"time"
@ -91,7 +92,7 @@ func processSSHCommand(payload []byte, connection *Connection, enabledSSHCommand
name, args, err := parseCommandPayload(msg.Command)
connection.Log(logger.LevelDebug, "new ssh command: %q args: %v num args: %d user: %s, error: %v",
name, args, len(args), connection.User.Username, err)
if err == nil && util.Contains(enabledSSHCommands, name) {
if err == nil && slices.Contains(enabledSSHCommands, name) {
connection.command = msg.Command
if name == scpCmdName && len(args) >= 2 {
connection.SetProtocol(common.ProtocolSCP)
@ -139,9 +140,9 @@ func (c *sshCommand) handle() (err error) {
defer common.Connections.Remove(c.connection.GetID())
c.connection.UpdateLastActivity()
if util.Contains(sshHashCommands, c.command) {
if slices.Contains(sshHashCommands, c.command) {
return c.handleHashCommands()
} else if util.Contains(systemCommands, c.command) {
} else if slices.Contains(systemCommands, c.command) {
command, err := c.getSystemCommand()
if err != nil {
return c.sendErrorResponse(err)
@ -429,11 +430,11 @@ func (c *sshCommand) getSystemCommand() (systemCommand, error) {
// If the user cannot create symlinks we add the option --munge-links, if it is not
// already set. This should make symlinks unusable (but manually recoverable)
if c.connection.User.HasPerm(dataprovider.PermCreateSymlinks, c.getDestPath()) {
if !util.Contains(args, "--safe-links") {
if !slices.Contains(args, "--safe-links") {
args = append([]string{"--safe-links"}, args...)
}
} else {
if !util.Contains(args, "--munge-links") {
if !slices.Contains(args, "--munge-links") {
args = append([]string{"--munge-links"}, args...)
}
}

View file

@ -19,6 +19,7 @@ import (
"context"
"errors"
"fmt"
"slices"
"sync"
"time"
@ -27,7 +28,6 @@ import (
"golang.org/x/oauth2/microsoft"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util"
)
// Supported OAuth2 providers
@ -56,7 +56,7 @@ type OAuth2Config struct {
// Validate validates and initializes the configuration
func (c *OAuth2Config) Validate() error {
if !util.Contains(supportedOAuth2Providers, c.Provider) {
if !slices.Contains(supportedOAuth2Providers, c.Provider) {
return fmt.Errorf("smtp oauth2: unsupported provider %d", c.Provider)
}
if c.ClientID == "" {

View file

@ -128,16 +128,6 @@ var bytesSizeTable = map[string]uint64{
"e": eByte,
}
// Contains reports whether v is present in elems.
func Contains[T comparable](elems []T, v T) bool {
for _, s := range elems {
if v == s {
return true
}
}
return false
}
// Remove removes an element from a string slice and
// returns the modified slice
func Remove(elems []string, val string) []string {

View file

@ -24,6 +24,7 @@ import (
"os"
"path"
"path/filepath"
"slices"
"strings"
"time"
@ -34,7 +35,6 @@ import (
"github.com/sftpgo/sdk"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util"
)
const (
@ -475,7 +475,7 @@ func (fs *OsFs) findNonexistentDirs(filePath string) ([]string, error) {
for fs.IsNotExist(err) {
results = append(results, parent)
parent = filepath.Dir(parent)
if util.Contains(results, parent) {
if slices.Contains(results, parent) {
break
}
_, err = os.Stat(parent)

View file

@ -30,6 +30,7 @@ import (
"os"
"path"
"path/filepath"
"slices"
"sort"
"strings"
"sync"
@ -161,7 +162,7 @@ func (fs *S3Fs) Stat(name string) (os.FileInfo, error) {
if err == nil {
// Some S3 providers (like SeaweedFS) remove the trailing '/' from object keys.
// So we check some common content types to detect if this is a "directory".
isDir := util.Contains(s3DirMimeTypes, util.GetStringFromPointer(obj.ContentType))
isDir := slices.Contains(s3DirMimeTypes, util.GetStringFromPointer(obj.ContentType))
if util.GetIntFromPointer(obj.ContentLength) == 0 && !isDir {
_, err = fs.headObject(name + "/")
isDir = err == nil

View file

@ -28,6 +28,7 @@ import (
"os"
"path"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
@ -125,7 +126,7 @@ func (c *SFTPFsConfig) isEqual(other SFTPFsConfig) bool {
return false
}
for _, fp := range c.Fingerprints {
if !util.Contains(other.Fingerprints, fp) {
if !slices.Contains(other.Fingerprints, fp) {
return false
}
}
@ -954,12 +955,12 @@ func (c *sftpConnection) openConnNoLock() error {
User: c.config.Username,
HostKeyCallback: func(_ string, _ net.Addr, key ssh.PublicKey) error {
fp := ssh.FingerprintSHA256(key)
if util.Contains(sftpFingerprints, fp) {
if slices.Contains(sftpFingerprints, fp) {
if allowSelfConnections == 0 {
logger.Log(logger.LevelError, c.logSender, "", "SFTP self connections not allowed")
return ErrSFTPLoop
}
if util.Contains(c.config.forbiddenSelfUsernames, c.config.Username) {
if slices.Contains(c.config.forbiddenSelfUsernames, c.config.Username) {
logger.Log(logger.LevelError, c.logSender, "",
"SFTP loop or nested local SFTP folders detected, username %q, forbidden usernames: %+v",
c.config.Username, c.config.forbiddenSelfUsernames)

View file

@ -24,6 +24,7 @@ import (
"path"
"path/filepath"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@ -764,7 +765,7 @@ func (c *AzBlobFsConfig) validate() error {
if err := c.checkPartSizeAndConcurrency(); err != nil {
return err
}
if !util.Contains(validAzAccessTier, c.AccessTier) {
if !slices.Contains(validAzAccessTier, c.AccessTier) {
return fmt.Errorf("invalid access tier %q, valid values: \"''%v\"", c.AccessTier, strings.Join(validAzAccessTier, ", "))
}
return nil

View file

@ -23,6 +23,7 @@ import (
"net/http"
"os"
"path"
"slices"
"sync/atomic"
"time"
@ -447,7 +448,7 @@ func (f *webDavFile) Patch(patches []webdav.Proppatch) ([]webdav.Propstat, error
pstat := webdav.Propstat{}
for _, p := range patch.Props {
if status == http.StatusForbidden && !hasError {
if !patch.Remove && util.Contains(lastModifiedProps, p.XMLName.Local) {
if !patch.Remove && slices.Contains(lastModifiedProps, p.XMLName.Local) {
parsed, err := parseTime(util.BytesToString(p.InnerXML))
if err != nil {
f.Connection.Log(logger.LevelWarn, "unsupported last modification time: %q, err: %v",

View file

@ -26,6 +26,7 @@ import (
"path"
"path/filepath"
"runtime/debug"
"slices"
"strings"
"time"
@ -346,7 +347,7 @@ func (s *webDavServer) validateUser(user *dataprovider.User, r *http.Request, lo
user.Username, user.HomeDir)
return connID, fmt.Errorf("cannot login user with invalid home dir: %q", user.HomeDir)
}
if util.Contains(user.Filters.DeniedProtocols, common.ProtocolWebDAV) {
if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolWebDAV) {
logger.Info(logSender, connectionID, "cannot login user %q, protocol DAV is not allowed", user.Username)
return connID, fmt.Errorf("protocol DAV is not allowed for user %q", user.Username)
}